diff --git a/.dockerignore b/.dockerignore index 5171c540..0f524ab0 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,4 @@ node_modules -npm-debug.log \ No newline at end of file +npm-debug.log +!chain/out +!chain/out/**/*.json \ No newline at end of file diff --git a/.env.example b/.env.example index 785a8a96..bec8e4db 100644 --- a/.env.example +++ b/.env.example @@ -29,4 +29,4 @@ CONVERTIBLES_FACET= EQUITY_COMPENSATION_FACET= STOCK_PLAN_FACET= WARRANT_FACET= -STAKEHOLDER_NFT_FACET= +STAKEHOLDER_NFT_FACET= \ No newline at end of file diff --git a/.github/workflows/deploy.dev.yaml b/.github/workflows/deploy.dev.yaml new file mode 100644 index 00000000..ed066a89 --- /dev/null +++ b/.github/workflows/deploy.dev.yaml @@ -0,0 +1,137 @@ +name: Deployment (Dev) + +on: + push: + branches: + - dev + - adam/setup-cicd + +jobs: + build: + name: Build, Test, and Deploy + environment: dev + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + submodules: true + fetch-depth: 0 + + # Setup and cache dependencies + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: "18" + cache: "yarn" + + - name: Install Node Dependencies + run: yarn install --frozen-lockfile + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Run Forge Install Script + run: chmod +x setup.sh && ./setup.sh + + # Run all tests and checks + - name: Run Forge Tests + run: cd chain && forge test --summary + + - name: Run ESLint + run: yarn lint:check + + - name: Check Formatting + run: yarn format:check + + # Build and Deploy + - name: Deploy + shell: bash + env: + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + HOST: ${{ secrets.LIGHTSAIL_INSTANCE_PUBLIC_IP_DEV }} + PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + ETHERSCAN_L2_API_KEY: ${{ secrets.ETHERSCAN_L2_API_KEY }} + SENTRY_DSN: ${{ secrets.SENTRY_DSN }} + DATABASE_URL: ${{ secrets.DATABASE_URL }} + RPC_URL: ${{ secrets.RPC_URL }} + CHAIN_ID: ${{ secrets.CHAIN_ID }} + + run: | + # Generate timestamp for deployment + DEPLOY_TIME=$(date +%s) + echo "DEPLOY_TIME: $DEPLOY_TIME" + + # Save SSH key and set permissions + echo "$SSH_PRIVATE_KEY" > deploy_key + chmod 600 deploy_key + + # Create a temp directory for deployment + DEPLOY_DIR="/tmp/deploy-${DEPLOY_TIME}" + mkdir -p $DEPLOY_DIR + + # Copy necessary files to temp directory + echo "Preparing deployment files..." + cp -r . $DEPLOY_DIR/ + + # Sync files to server + echo "Syncing files to server..." + rsync -az --delete \ + --exclude='node_modules' \ + --exclude='.git' \ + --exclude='deploy_key' \ + --include='chain/out' \ + --include='chain/out/**' \ + -e "ssh -i deploy_key -o StrictHostKeyChecking=no" \ + $DEPLOY_DIR/ \ + ubuntu@"$HOST":/home/ubuntu/app-${DEPLOY_TIME} + + # Execute deployment on server + ssh -i deploy_key -o StrictHostKeyChecking=no ubuntu@"$HOST" " + sudo su && \ + cd /home/ubuntu/app-${DEPLOY_TIME} && \ + echo 'Building image on host...' && \ + # Source the functions + source ./scripts/docker_container_utils.sh && \ + docker build -t ocp-dev:${DEPLOY_TIME} -f Dockerfile.dev . && \ + + # Initial cleanup + echo 'Cleaning up old resources...' && \ + docker ps -q --filter 'publish=8081' | xargs -r docker rm -f && \ + docker ps -q --filter 'publish=8082' | xargs -r docker rm -f && \ + docker container prune -f && \ + docker image prune -f && \ + + # Start new container + echo 'Starting new container...' && \ + CONTAINER_NAME=ocp-dev-${DEPLOY_TIME} && \ + + # Run container + docker run --name \$CONTAINER_NAME -d \ + --health-cmd='curl -f http://localhost:8080/health || exit 1' \ + --health-interval='2s' \ + --health-retries='3' \ + --health-timeout='5s' \ + --restart always \ + -e DOCKER_ENV='true' \ + -e NODE_ENV='development' \ + -e SENTRY_DSN='${SENTRY_DSN}' \ + -e DATABASE_URL='${DATABASE_URL}' \ + -e RPC_URL='${RPC_URL}' \ + -e CHAIN_ID='${CHAIN_ID}' \ + -e PORT=8080 \ + -e PRIVATE_KEY='${PRIVATE_KEY}' \ + -e ETHERSCAN_L2_API_KEY='${ETHERSCAN_L2_API_KEY}' \ + -v '/home/ubuntu/global-bundle.pem:/global-bundle.pem' \ + ocp-dev:${DEPLOY_TIME} && \ + + # Wait for container to be healthy + wait_for_health "\$CONTAINER_NAME" && \ + if [ \$? -eq 0 ]; then + handle_container_switch "\$CONTAINER_NAME" "${DEPLOY_TIME}" "dev" + else + handle_failed_deployment "\$CONTAINER_NAME" "${DEPLOY_TIME}" "dev" + fi + " diff --git a/.github/workflows/deploy.prod.yaml b/.github/workflows/deploy.prod.yaml new file mode 100644 index 00000000..eeb8e103 --- /dev/null +++ b/.github/workflows/deploy.prod.yaml @@ -0,0 +1,132 @@ +name: Deployment (Prod) + +on: + push: + branches: + - main + +jobs: + build: + name: Build, Test, and Deploy + environment: prod + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + submodules: true + fetch-depth: 0 + + # Setup and cache dependencies + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: "18" + cache: "yarn" + + - name: Install Node Dependencies + run: yarn install --frozen-lockfile + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Run Forge Install Script + run: chmod +x setup.sh && ./setup.sh + + # Run all tests and checks + - name: Run Forge Tests + run: cd chain && forge test --summary + + - name: Run ESLint + run: yarn lint:check + + - name: Check Formatting + run: yarn format:check + + - name: Deploy + shell: bash + env: + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + HOST: ${{ secrets.LIGHTSAIL_INSTANCE_PUBLIC_IP_PROD }} + PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + ETHERSCAN_L2_API_KEY: ${{ secrets.ETHERSCAN_L2_API_KEY }} + SENTRY_DSN: ${{ secrets.SENTRY_DSN }} + DATABASE_URL: ${{ secrets.DATABASE_URL }} + RPC_URL: ${{ secrets.RPC_URL }} + CHAIN_ID: ${{ secrets.CHAIN_ID }} + + run: | + # Generate timestamp for deployment + DEPLOY_TIME=$(date +%s) + # Save SSH key and set permissions + echo "$SSH_PRIVATE_KEY" > deploy_key + chmod 600 deploy_key + + # Create a temp directory for deployment + DEPLOY_DIR="/tmp/deploy-${DEPLOY_TIME}" + mkdir -p $DEPLOY_DIR + + # Copy necessary files to temp directory + echo "Preparing deployment files..." + cp -r . $DEPLOY_DIR/ + + # Sync files to server + echo "Syncing files to server..." + rsync -az --delete \ + --exclude='node_modules' \ + --exclude='.git' \ + --exclude='deploy_key' \ + --include='chain/out' \ + --include='chain/out/**' \ + -e "ssh -i deploy_key -o StrictHostKeyChecking=no" \ + $DEPLOY_DIR/ \ + ubuntu@"$HOST":/home/ubuntu/app-${DEPLOY_TIME} + + sudo su && \ + cd /home/ubuntu/app-${DEPLOY_TIME} && \ + echo "Building image on host..." && \ + # Source the functions + source ./scripts/docker_container_utils.sh && \ + docker build -t ocp-prod:${DEPLOY_TIME} -f Dockerfile.prod . && \ + + # Initial cleanup of dangling images and stopped containers + echo "Cleaning up old resources..." && \ + docker ps -q --filter "publish=8081" | xargs -r docker rm -f && \ + docker ps -q --filter "publish=8082" | xargs -r docker rm -f && \ + docker container prune -f && \ + docker image prune -f && \ + + # Start new container on different port + echo "Starting new container..." && \ + CONTAINER_NAME=ocp-prod-${DEPLOY_TIME} && \ + NEW_PORT=$(get_port) + echo "Using port: $NEW_PORT" && \ + + docker run --name $CONTAINER_NAME -d \ + -p ${NEW_PORT}:8080 \ + --health-cmd="curl -f http://localhost:8080/health || exit 1" \ + --health-interval=2s \ + --health-retries=3 \ + --health-timeout=5s \ + --restart always \ + -e DOCKER_ENV="true" \ + -e NODE_ENV="production" \ + -e SENTRY_DSN="${{ secrets.SENTRY_DSN }}" \ + -e DATABASE_URL="${{ secrets.DATABASE_URL }}" \ + -e RPC_URL="${{ secrets.RPC_URL }}" \ + -e CHAIN_ID="${{ secrets.CHAIN_ID }}" \ + -e PORT=8080 \ + -e PRIVATE_KEY="${{ secrets.PRIVATE_KEY }}" \ + -e ETHERSCAN_L2_API_KEY="${{ secrets.ETHERSCAN_L2_API_KEY }}" \ + -v "/home/ubuntu/global-bundle.pem:/global-bundle.pem" \ + ocp-prod:${DEPLOY_TIME} && \ + + # Wait for container to be healthy + wait_for_health "$CONTAINER_NAME" && \ + + # If container is healthy, switch traffic + if [ $? -eq 0 ]; then + handle_container_switch "$CONTAINER_NAME" "${DEPLOY_TIME}" "prod" + else + handle_failed_deployment "$CONTAINER_NAME" "${DEPLOY_TIME}" "prod" + fi diff --git a/.github/workflows/integrate.yaml b/.github/workflows/integrate.yaml new file mode 100644 index 00000000..445a39f8 --- /dev/null +++ b/.github/workflows/integrate.yaml @@ -0,0 +1,48 @@ +name: Integration Tests + +on: + pull_request: + branches: + - "*" + - "!main" + - "dev" + types: [opened, synchronize, reopened] + +jobs: + test: + name: Build and Test + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + submodules: true + fetch-depth: 0 + + # Install only ESLint dependencies + - name: Install ESLint Dependencies + run: | + yarn add -D @eslint/js@latest \ + @typescript-eslint/eslint-plugin@latest \ + @typescript-eslint/parser@latest \ + eslint-plugin-import@latest \ + globals@latest + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Run Forge Install Script + run: | + chmod +x setup.sh && ./setup.sh + + # Run all tests and checks + - name: Run Forge Tests + run: cd chain && forge test --summary + + - name: Run ESLint + run: npx eslint "src/**/*.{js,ts,mjs,mts}" + + - name: Check Formatting + run: npx prettier --check "src/**/*.{js,mjs,ts,mts,json,md}" diff --git a/.gitignore b/.gitignore index e36ae1b1..f705ca0a 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ tsconfig.tsbuildinfo .env.* chain/out chain/lib/* +solana/* +*.ignore.js diff --git a/.gitmodules b/.gitmodules index c2a0256f..53046645 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ +[submodule "ocf"] + path = ocf + url = https://github.com/Open-Cap-Table-Coalition/Open-Cap-Format-OCF [submodule "chain/lib/forge-std"] path = chain/lib/forge-std url = https://github.com/foundry-rs/forge-std @@ -10,6 +13,3 @@ [submodule "chain/lib/diamond-3-hardhat"] path = chain/lib/diamond-3-hardhat url = https://github.com/mudgen/diamond-3-hardhat -[submodule "ocf"] - path = ocf - url = https://github.com/Open-Cap-Table-Coalition/Open-Cap-Format-OCF diff --git a/.husky/pre-commit b/.husky/pre-commit index b0e94d79..f094af21 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1,2 @@ yarn flightcheck +cd chain && forge fmt \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index 1a87d25f..cea04a68 100644 --- a/.prettierrc +++ b/.prettierrc @@ -5,7 +5,5 @@ "bracketSpacing": true, "arrowParens": "always", "printWidth": 150, - "endOfLine": "auto", - "formatOnSave": true, - "editor.formatOnSave": true + "endOfLine": "auto" } \ No newline at end of file diff --git a/.solhintrc b/.solhintrc deleted file mode 100644 index c43f418d..00000000 --- a/.solhintrc +++ /dev/null @@ -1,39 +0,0 @@ -{ - "extends": "solhint:recommended", - "rules": { - "compiler-version": [ - "error", - "^0.8.0" - ], - "func-visibility": [ - "error", - { - "ignoreConstructors": true - } - ], - "not-rely-on-time": "off", - "no-empty-blocks": "warn", - "max-line-length": [ - "warn", - 120 - ], - "reason-string": [ - "warn", - { - "maxLength": 64 - } - ], - "state-visibility": "error", - "var-name-mixedcase": "warn", - "func-name-mixedcase": "warn", - "private-vars-leading-underscore": [ - "warn", - { - "strict": false - } - ], - "ordering": "warn", - "reentrancy": "error", - "avoid-low-level-calls": "warn" - } -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index ceea13a5..a03025c2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,10 @@ { "solidity.packageDefaultDependenciesDirectory": "chain/lib", "solidity.packageDefaultDependenciesContractsDirectory": "chain/src", - "solidity.formatter": "prettier", + "solidity.formatter": "forge", "solidity.compileUsingRemoteVersion": "v0.8.20", "solidity.remappings": ["@openzeppelin/=chain/lib/openzeppelin-contracts/", "@diamond/=chain/src/lib/diamond/"], - "solidity.linter": "solium", + "solidity.linter": "solhint", "solidity.enabledAsYouTypeCompilationErrorCheck": true, "editor.formatOnSave": true, "[solidity]": { diff --git a/Dockerfile.dev b/Dockerfile.dev index 31392c67..c2d6384f 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -4,11 +4,12 @@ FROM node:18 # Set the working directory WORKDIR /app -COPY . . - -# Install dependencies and setup +# Copy package files first to leverage Docker cache +COPY package.json yarn.lock ./ RUN yarn install +# Then copy the rest of the files +COPY . . + EXPOSE 8080 -# Specify the command to run on container start CMD ["yarn", "dev"] diff --git a/Dockerfile.prod b/Dockerfile.prod index fb633c29..98fb3d23 100644 --- a/Dockerfile.prod +++ b/Dockerfile.prod @@ -4,12 +4,12 @@ FROM node:18 # Set the working directory WORKDIR /app -# COPY ./chain/out ./chain/out -COPY . . - -# Install dependencies and setup +# Copy package files first to leverage Docker cache +COPY package.json yarn.lock ./ RUN yarn install +# Then copy the rest of the files +COPY . . + EXPOSE 8080 -# Specify the command to run on container start CMD ["yarn", "prod"] diff --git a/LICENSE b/LICENSE index 01d7134c..375ab8a8 100644 --- a/LICENSE +++ b/LICENSE @@ -20,4 +20,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +SOFTWARE. \ No newline at end of file diff --git a/chain/chain/test/StockTransfer.t.sol b/chain/chain/test/StockTransfer.t.sol new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/chain/chain/test/StockTransfer.t.sol @@ -0,0 +1 @@ + diff --git a/chain/foundry.toml b/chain/foundry.toml index 0d8025f5..c03f491f 100644 --- a/chain/foundry.toml +++ b/chain/foundry.toml @@ -1,18 +1,50 @@ +# Development profile - Used for testing and local development [profile.default] -src = "src" -out = "out" -libs = ["lib"] -solc_version = '0.8.20' -bytecode_hash = "none" -cbor_metadata = false +src = "src" # Source directory for Solidity contracts +out = "out" # Output directory for compiled artifacts +libs = ["lib"] # External library directories +solc_version = '0.8.20' # Solidity compiler version +bytecode_hash = "none" # Skip bytecode hashing for faster compilation +cbor_metadata = false # Disable CBOR metadata to reduce contract size +optimizer = false # Disable optimizer for faster compilation +via_ir = false # Disable IR pipeline for faster compilation +optimizer_runs = 200 # Lower number for faster compilation +parallel = true # Enable parallel compilation for faster builds +ffi = true # Enable FFI for development/testing -[rpc_endpoints] -rpc_url = "${RPC_URL}" +# Production profile - Used for testnet/mainnet deployments +[profile.production] +src = "src" # Source directory for Solidity contracts +out = "out" # Output directory for compiled artifacts +libs = ["lib"] # External library directories +solc_version = '0.8.20' # Solidity compiler version +bytecode_hash = "ipfs" # Use IPFS for better contract verification +cbor_metadata = false # Disable CBOR metadata to reduce contract size +optimizer = true # Enable optimizer for gas optimization +via_ir = true # Enable IR-based compilation for better optimization +optimizer_runs = 1000000 # Optimize for many contract calls +extra_output = [ # Additional outputs for verification and debugging + "storageLayout", # Storage layout for debugging + "metadata" # Contract metadata for verification +] +build_info = true # Generate build info for contract verification +ffi = false # Disable foreign function interface for security +parallel = true # Enable parallel compilation -[etherscan] -optimism_goerli_etherscan = { key = "${ETHERSCAN_L2_API_KEY}", chain = "sepolia" } +# Code formatting settings +[fmt] +line_length = 120 # Maximum line length +tab_width = 4 # Number of spaces per tab +bracket_spacing = true # Spaces inside brackets +multiline_func_header = "all" # Format function headers across multiple lines +number_underscore = "thousands" # Add underscores to large numbers +quote_style = "double" # Use double quotes for strings +ignore = [] # Files to ignore when formatting +# Network configuration +[rpc_endpoints] +rpc_url = "${RPC_URL}" # RPC endpoint from environment variable # See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/chain/lib/forge-std b/chain/lib/forge-std index 1eea5bae..3b20d60d 160000 --- a/chain/lib/forge-std +++ b/chain/lib/forge-std @@ -1 +1 @@ -Subproject commit 1eea5bae12ae557d589f9f0f0edae2faa47cb262 +Subproject commit 3b20d60d14b343ee4f908cb8079495c07f5e8981 diff --git a/chain/lib/openzeppelin-contracts b/chain/lib/openzeppelin-contracts index 69c8def5..2141d3fa 160000 --- a/chain/lib/openzeppelin-contracts +++ b/chain/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit 69c8def5f222ff96f2b5beff05dfba996368aa79 +Subproject commit 2141d3faf5ac28a40a8619d812a8c72cc609ac81 diff --git a/chain/lib/openzeppelin-contracts-upgradeable b/chain/lib/openzeppelin-contracts-upgradeable index fa525310..3d4c0d57 160000 --- a/chain/lib/openzeppelin-contracts-upgradeable +++ b/chain/lib/openzeppelin-contracts-upgradeable @@ -1 +1 @@ -Subproject commit fa525310e45f91eb20a6d3baa2644be8e0adba31 +Subproject commit 3d4c0d5741b131c231e558d7a6213392ab3672a5 diff --git a/chain/out/ConvertiblesFacet.sol/ConvertiblesFacet.json b/chain/out/ConvertiblesFacet.sol/ConvertiblesFacet.json deleted file mode 100644 index 9054f754..00000000 --- a/chain/out/ConvertiblesFacet.sol/ConvertiblesFacet.json +++ /dev/null @@ -1,294 +0,0 @@ -{ - "abi": [ - { - "type": "function", - "name": "getConvertiblePosition", - "inputs": [ - { - "name": "securityId", - "type": "bytes16", - "internalType": "bytes16" - } - ], - "outputs": [ - { - "name": "", - "type": "tuple", - "internalType": "struct ConvertibleActivePosition", - "components": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "investment_amount", - "type": "uint256", - "internalType": "uint256" - } - ] - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "issueConvertible", - "inputs": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "investment_amount", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "security_id", - "type": "bytes16", - "internalType": "bytes16" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "event", - "name": "TxCreated", - "inputs": [ - { - "name": "txType", - "type": "uint8", - "indexed": false, - "internalType": "enum TxType" - }, - { - "name": "txData", - "type": "bytes", - "indexed": false, - "internalType": "bytes" - } - ], - "anonymous": false - }, - { - "type": "error", - "name": "InvalidAmount", - "inputs": [] - }, - { - "type": "error", - "name": "NoStakeholder", - "inputs": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - } - ] - } - ], - "bytecode": { - "object": "0x60808060405234610016576103d9908161001c8239f35b600080fdfe60806040908082526004908136101561001757600080fd5b60009160e0918335831c928363ddd8ed5e146100d25750505063e9f4a2281461003f57600080fd5b346100cf5760203660031901126100cf578161005961038b565b918060208351610068816103a7565b82815201526001600160801b031980931681527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c016020522090602060018451926100b1846103a7565b845460801b1693848452015491019081528251918252516020820152f35b80fd5b8592935034610387576060366003190112610387576100ef61038b565b60443593906024906001600160801b031980871691833591838903610383577f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfc8054600019811461037157600101905580821698898c526020987f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf78a52878d20541561035d5750831561034d57865160018a898f8e61018d866103a7565b85527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c01838601938a85528b83525220925160801c8584541617835551910155898c527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c008952868c20908154916801000000000000000083101561033b5760018301808255831015610329578d52898d208260011c01916001600160801b03608084549260071b169260801c831b921b1916179055838b527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c028852858b209160801c908254161790558351968688015283870152606086015260608552608085019585871067ffffffffffffffff881117610318575050908185879352600a865260a08501528351928360c0860152825b848110610303578486018301849052837f88aa35e399ceb8d6b2e810abb3df607c140128e7c089c428f212598bda6a7eb7601f8701601f1916880188900360600189a180f35b858101808301519084015287935081016102bd565b634e487b7160e01b88526041905286fd5b634e487b7160e01b8e5260328d52878efd5b634e487b7160e01b8e5260418d52878efd5b865163162908e360e11b81528b90fd5b63b4586dfb60e01b81528b81018b90528690fd5b634e487b7160e01b8d5260118c52868dfd5b8a80fd5b8480fd5b600435906001600160801b0319821682036103a257565b600080fd5b6040810190811067ffffffffffffffff8211176103c357604052565b634e487b7160e01b600052604160045260246000fd", - "sourceMap": "295:1262:68:-:0;;;;;;;;;;;;;;;;;", - "linkReferences": {} - }, - "deployedBytecode": { - "object": "0x60806040908082526004908136101561001757600080fd5b60009160e0918335831c928363ddd8ed5e146100d25750505063e9f4a2281461003f57600080fd5b346100cf5760203660031901126100cf578161005961038b565b918060208351610068816103a7565b82815201526001600160801b031980931681527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c016020522090602060018451926100b1846103a7565b845460801b1693848452015491019081528251918252516020820152f35b80fd5b8592935034610387576060366003190112610387576100ef61038b565b60443593906024906001600160801b031980871691833591838903610383577f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfc8054600019811461037157600101905580821698898c526020987f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf78a52878d20541561035d5750831561034d57865160018a898f8e61018d866103a7565b85527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c01838601938a85528b83525220925160801c8584541617835551910155898c527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c008952868c20908154916801000000000000000083101561033b5760018301808255831015610329578d52898d208260011c01916001600160801b03608084549260071b169260801c831b921b1916179055838b527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c028852858b209160801c908254161790558351968688015283870152606086015260608552608085019585871067ffffffffffffffff881117610318575050908185879352600a865260a08501528351928360c0860152825b848110610303578486018301849052837f88aa35e399ceb8d6b2e810abb3df607c140128e7c089c428f212598bda6a7eb7601f8701601f1916880188900360600189a180f35b858101808301519084015287935081016102bd565b634e487b7160e01b88526041905286fd5b634e487b7160e01b8e5260328d52878efd5b634e487b7160e01b8e5260418d52878efd5b865163162908e360e11b81528b90fd5b63b4586dfb60e01b81528b81018b90528690fd5b634e487b7160e01b8d5260118c52868dfd5b8a80fd5b8480fd5b600435906001600160801b0319821682036103a257565b600080fd5b6040810190811067ffffffffffffffff8211176103c357604052565b634e487b7160e01b600052604160045260246000fd", - "sourceMap": "295:1262:68:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;295:1262:68;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;295:1262:68;;;;;;1496:40;295:1262;;;;;1496:40;295:1262;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;295:1262:68;;;;;;:::i;:::-;;;;;;;-1:-1:-1;;;;;;295:1262:68;;;;;;;;;;;;492:8;295:1262;;-1:-1:-1;;295:1262:68;;;;;;;;;;;;;;;;;686:19:77;295:1262:68;;;;;;686:40:77;682:107;;1477:11;;;1473:39;;295:1262:68;;;;;;;;;;:::i;:::-;;;667:40;723:131;;;295:1262;;;;;;;;;;;;;;;;;;;;;;;;;;;667:29;295:1262;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;295:1262:68;;;;;;;;;;;;;;;;;;;;;;1045:51;295:1262;;;;;;;;;;;;;;;;;1188:58;;;;295:1262;;;;;;;;;;1188:58;;295:1262;;;;;;;;;;;;;;;;;;;;;1274:27;295:1262;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1098:25:65;295:1262:68;;;-1:-1:-1;;295:1262:68;;;1098:25:65;;;295:1262:68;1098:25:65;295:1262:68;1098:25:65;295:1262:68;;;;;;;;;;;;;;;;-1:-1:-1;295:1262:68;;;;;-1:-1:-1;;;295:1262:68;;;;;;;;-1:-1:-1;;;295:1262:68;;;;;;;;;-1:-1:-1;;;295:1262:68;;;;;;;;1473:39:77;295:1262:68;;-1:-1:-1;;;1497:15:77;;295:1262:68;;1497:15:77;682:107;-1:-1:-1;;;749:29:77;;;;;295:1262:68;;;749:29:77;;;295:1262:68;-1:-1:-1;;;295:1262:68;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;295:1262:68;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;", - "linkReferences": {} - }, - "methodIdentifiers": { - "getConvertiblePosition(bytes16)": "e9f4a228", - "issueConvertible(bytes16,uint256,bytes16)": "ddd8ed5e" - }, - "rawMetadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"InvalidAmount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"}],\"name\":\"NoStakeholder\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"enum TxType\",\"name\":\"txType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"txData\",\"type\":\"bytes\"}],\"name\":\"TxCreated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"securityId\",\"type\":\"bytes16\"}],\"name\":\"getConvertiblePosition\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"},{\"internalType\":\"uint256\",\"name\":\"investment_amount\",\"type\":\"uint256\"}],\"internalType\":\"struct ConvertibleActivePosition\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"},{\"internalType\":\"uint256\",\"name\":\"investment_amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes16\",\"name\":\"security_id\",\"type\":\"bytes16\"}],\"name\":\"issueConvertible\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/lib/diamond/facets/ConvertiblesFacet.sol\":\"ConvertiblesFacet\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"appendCBOR\":false,\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@diamond/=src/lib/diamond/\",\":diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/\",\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\",\":openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/\"],\"viaIR\":true},\"sources\":{\"src/lib/Structs.sol\":{\"keccak256\":\"0x404e740ae677baf5cc57884ee32d9accb367ae58f56a23f4e59b4f2987ae5100\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://349ec5d7e23684f71836001d70c012e59c0f2c94c9fa1fae83f85eeccda1fe52\",\"dweb:/ipfs/QmTVT5hzGePT8FxFxWhX77bH1DS1Z8iu8NAfAdn4PfsyCh\"]},\"src/lib/diamond/DiamondTxHelper.sol\":{\"keccak256\":\"0xe72ba093a5b16735d0193624d5b6585deb2cfe7e419a468c90fd70317fccf5e6\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://4ae3bd1ccbe005c63bed07e255c2b25c9bfdb37b9d85bb75f8f65cfeb0f6df98\",\"dweb:/ipfs/QmbTpzVeLkvthzpasQLvuvAfwE3g4sMsjEfpRBNwyG4eMQ\"]},\"src/lib/diamond/Storage.sol\":{\"keccak256\":\"0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d\",\"dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL\"]},\"src/lib/diamond/Structs.sol\":{\"keccak256\":\"0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab\",\"dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD\"]},\"src/lib/diamond/facets/ConvertiblesFacet.sol\":{\"keccak256\":\"0x571d0b98da033ca9b1726bbc2dbe293c906a03fbc295b5a03a90ce173c1a723f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6402bd044e8c8a738c0f932609c2f0e16eb1eeab58340c6c4b512f773d72e3e7\",\"dweb:/ipfs/QmZgJCKhjnsCFM8kvVPGxtzUrk7atjA2CLVi6Cbpf867Ec\"]},\"src/lib/diamond/libraries/ValidationLib.sol\":{\"keccak256\":\"0xfcd772b0670acded810468d772729715e3cd4db25c02a0da77c7445903e1cc5f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d572672df16392825ed86c32a53453247f62b481787db0498993f857623e1ee6\",\"dweb:/ipfs/Qmbpoa5EZ7yRcB1mZkmb5nhfxhhFY9g8tMJxPf9ZrzfNfL\"]}},\"version\":1}", - "metadata": { - "compiler": { - "version": "0.8.20+commit.a1b79de6" - }, - "language": "Solidity", - "output": { - "abi": [ - { - "inputs": [], - "type": "error", - "name": "InvalidAmount" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - } - ], - "type": "error", - "name": "NoStakeholder" - }, - { - "inputs": [ - { - "internalType": "enum TxType", - "name": "txType", - "type": "uint8", - "indexed": false - }, - { - "internalType": "bytes", - "name": "txData", - "type": "bytes", - "indexed": false - } - ], - "type": "event", - "name": "TxCreated", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "securityId", - "type": "bytes16" - } - ], - "stateMutability": "view", - "type": "function", - "name": "getConvertiblePosition", - "outputs": [ - { - "internalType": "struct ConvertibleActivePosition", - "name": "", - "type": "tuple", - "components": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - }, - { - "internalType": "uint256", - "name": "investment_amount", - "type": "uint256" - } - ] - } - ] - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - }, - { - "internalType": "uint256", - "name": "investment_amount", - "type": "uint256" - }, - { - "internalType": "bytes16", - "name": "security_id", - "type": "bytes16" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "issueConvertible" - } - ], - "devdoc": { - "kind": "dev", - "methods": {}, - "version": 1 - }, - "userdoc": { - "kind": "user", - "methods": {}, - "version": 1 - } - }, - "settings": { - "remappings": [ - "@diamond/=src/lib/diamond/", - "diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/", - "ds-test/=lib/forge-std/lib/ds-test/src/", - "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", - "forge-std/=lib/forge-std/src/", - "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", - "openzeppelin-contracts/=lib/openzeppelin-contracts/", - "openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/" - ], - "optimizer": { - "enabled": true, - "runs": 200 - }, - "metadata": { - "bytecodeHash": "none", - "appendCBOR": false - }, - "compilationTarget": { - "src/lib/diamond/facets/ConvertiblesFacet.sol": "ConvertiblesFacet" - }, - "evmVersion": "paris", - "libraries": {}, - "viaIR": true - }, - "sources": { - "src/lib/Structs.sol": { - "keccak256": "0x404e740ae677baf5cc57884ee32d9accb367ae58f56a23f4e59b4f2987ae5100", - "urls": [ - "bzz-raw://349ec5d7e23684f71836001d70c012e59c0f2c94c9fa1fae83f85eeccda1fe52", - "dweb:/ipfs/QmTVT5hzGePT8FxFxWhX77bH1DS1Z8iu8NAfAdn4PfsyCh" - ], - "license": "MIT" - }, - "src/lib/diamond/DiamondTxHelper.sol": { - "keccak256": "0xe72ba093a5b16735d0193624d5b6585deb2cfe7e419a468c90fd70317fccf5e6", - "urls": [ - "bzz-raw://4ae3bd1ccbe005c63bed07e255c2b25c9bfdb37b9d85bb75f8f65cfeb0f6df98", - "dweb:/ipfs/QmbTpzVeLkvthzpasQLvuvAfwE3g4sMsjEfpRBNwyG4eMQ" - ], - "license": "MIT" - }, - "src/lib/diamond/Storage.sol": { - "keccak256": "0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f", - "urls": [ - "bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d", - "dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL" - ], - "license": "MIT" - }, - "src/lib/diamond/Structs.sol": { - "keccak256": "0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2", - "urls": [ - "bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab", - "dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/ConvertiblesFacet.sol": { - "keccak256": "0x571d0b98da033ca9b1726bbc2dbe293c906a03fbc295b5a03a90ce173c1a723f", - "urls": [ - "bzz-raw://6402bd044e8c8a738c0f932609c2f0e16eb1eeab58340c6c4b512f773d72e3e7", - "dweb:/ipfs/QmZgJCKhjnsCFM8kvVPGxtzUrk7atjA2CLVi6Cbpf867Ec" - ], - "license": "MIT" - }, - "src/lib/diamond/libraries/ValidationLib.sol": { - "keccak256": "0xfcd772b0670acded810468d772729715e3cd4db25c02a0da77c7445903e1cc5f", - "urls": [ - "bzz-raw://d572672df16392825ed86c32a53453247f62b481787db0498993f857623e1ee6", - "dweb:/ipfs/Qmbpoa5EZ7yRcB1mZkmb5nhfxhhFY9g8tMJxPf9ZrzfNfL" - ], - "license": "MIT" - } - }, - "version": 1 - }, - "id": 68 -} \ No newline at end of file diff --git a/chain/out/DiamondCapTableFactory.sol/DiamondCapTableFactory.json b/chain/out/DiamondCapTableFactory.sol/DiamondCapTableFactory.json deleted file mode 100644 index 8009c974..00000000 --- a/chain/out/DiamondCapTableFactory.sol/DiamondCapTableFactory.json +++ /dev/null @@ -1,968 +0,0 @@ -{ - "abi": [ - { - "type": "constructor", - "inputs": [ - { - "name": "_diamondCutFacet", - "type": "address", - "internalType": "address" - }, - { - "name": "_issuerFacet", - "type": "address", - "internalType": "address" - }, - { - "name": "_stakeholderFacet", - "type": "address", - "internalType": "address" - }, - { - "name": "_stockClassFacet", - "type": "address", - "internalType": "address" - }, - { - "name": "_stockFacet", - "type": "address", - "internalType": "address" - }, - { - "name": "_convertiblesFacet", - "type": "address", - "internalType": "address" - }, - { - "name": "_equityCompensationFacet", - "type": "address", - "internalType": "address" - }, - { - "name": "_stockPlanFacet", - "type": "address", - "internalType": "address" - }, - { - "name": "_warrantFacet", - "type": "address", - "internalType": "address" - }, - { - "name": "_stakeholderNFTFacet", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "capTables", - "inputs": [ - { - "name": "", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "convertiblesFacet", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "createCapTable", - "inputs": [ - { - "name": "id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "initialSharesAuthorized", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "diamondCutFacet", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "equityCompensationFacet", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "getCapTableCount", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "uint256", - "internalType": "uint256" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "issuerFacet", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "stakeholderFacet", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "stakeholderNFTFacet", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "stockClassFacet", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "stockFacet", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "stockPlanFacet", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "warrantFacet", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "event", - "name": "CapTableCreated", - "inputs": [ - { - "name": "capTable", - "type": "address", - "indexed": true, - "internalType": "address" - }, - { - "name": "issuerId", - "type": "bytes16", - "indexed": true, - "internalType": "bytes16" - } - ], - "anonymous": false - } - ], - "bytecode": { - "object": "0x6101c034620001f7576200221c38819003601f8101601f191683016001600160401b03811184821017620001fc5783928291604052833961014092839181010312620001f757620000508162000212565b6200005e6020830162000212565b6200006c6040840162000212565b936200007b6060850162000212565b906200008a6080860162000212565b6200009860a0870162000212565b620000a660c0880162000212565b91620000b560e0890162000212565b9461010099620000c78b8b0162000212565b97620000d8610120809c0162000212565b996001600160a01b03811615620001b25760805260a05260c05260e0528852865281526101609182526101809283526101a093845260405194611ff4968762000228883960805187818161018601526103ac015260a0518781816102140152610463015260c0518781816105180152610cdf015260e0518781816101cd01526105b80152518681816106440152610e400152518581816106e40152610d6d0152518481816107990152610d260152518381816108390152610db401525182818161013f01526108d90152518181816109790152610dfb0152f35b60405162461bcd60e51b815260206004820152601760248201527f496e76616c6964206469616d6f6e6443757446616365740000000000000000006044820152606490fd5b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620001f75756fe60808060405260043610156200001457600080fd5b600090813560e01c90816305a5ca2a1462000e2a5750806308f91d7b1462000de357806316adefe11462000d9c57806332b882a11462000d555780633d4ed9f71462000d0e57806350896c431462000cc75780636adff8fb14620002435780637f8a200c14620001fc57806383d3052e14620001b5578063a051e9b3146200016e578063b1dbd67f1462000127578063ba98c55414620000df5763f0f6d5a014620000be57600080fd5b34620000dc5780600319360112620000dc5760209054604051908152f35b80fd5b5034620000dc576020366003190112620000dc57600435908054821015620000dc5760206200010e8362000e6f565b905460405160039290921b1c6001600160a01b03168152f35b5034620000dc5780600319360112620000dc576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620000dc5780600319360112620000dc576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620000dc5780600319360112620000dc576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620000dc5780600319360112620000dc576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620000dc576040366003190112620000dc576004356001600160801b03198116810362000cc3576001600160801b0319811615158062000cb7575b1562000c7a578180604051620002968162000f02565b6015815274696e73696465206372656174654361705461626c6560581b6020820152604051620002f481620002e5602082019463104c13eb60e21b86526020602484015260448301906200100d565b03601f19810183528262000f3c565b51906a636f6e736f6c652e6c6f675afa5062000338604051620003178162000f02565b600c81526b036b9b39739b2b73232b91d160a51b602082015233906200104f565b6200037c6040516200034a8162000f02565b601881527f666163746f72792061646472657373202874686973293a200000000000000000602082015230906200104f565b604051610f3d9081810167ffffffffffffffff928282108483111762000c66576040918391620010b783393081527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316602082015203019084f0801562000c5b576040519161014083019081118382101762000c475760405260098252835b610120811062000c1a57506040516200041c8162000eba565b6002908181526040366020830137632f539c4560e01b6200043d8262000f5f565b52633f7a08e360e01b620004518262000f83565b5260405190620004618262000eba565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168252602082018790526040820152620004a48462000f5f565b52620004b08362000f5f565b50604051620004bf8162000f1f565b600381526060366020830137639e35325f60e01b620004de8262000f5f565b5263eafa8a5f60e01b620004f28262000f83565b52631b5b3e0960e21b620005068262000f94565b5260405190620005168262000eba565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168252602082018790526040820152620005598462000f83565b52620005658362000f83565b50604051620005748162000eba565b818152604036602083013763257eac7d60e11b620005928262000f5f565b52630cfdbcc560e31b620005a68262000f83565b5260405190620005b68262000eba565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168252602082018790526040820152620005f98462000f94565b52620006058362000f94565b50604051620006148162000f02565b60018152602036818301376355356b8b60e01b620006328262000f5f565b5260405190620006428262000eba565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168252602082018790526040820152620006858462000fa5565b52620006918362000fa5565b50604051620006a08162000eba565b8181526040366020830137636eec76af60e11b620006be8262000f5f565b52631d3e944560e31b620006d28262000f83565b5260405190620006e28262000eba565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168252602082018790526040820152620007258462000fb6565b52620007318362000fb6565b50604051620007408162000f1f565b600381526060366020830137630d062fcb60e01b6200075f8262000f5f565b52630d08aa0f60e01b620007738262000f83565b526335af1e9160e11b620007878262000f94565b5260405190620007978262000eba565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168252602082018790526040820152620007da8462000fc7565b52620007e68362000fc7565b50604051620007f58162000eba565b8181526040366020830137636b348f4160e01b620008138262000f5f565b52633871654760e21b620008278262000f83565b5260405190620008378262000eba565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031682526020820187905260408201526200087a8462000fd8565b52620008868362000fd8565b50604051620008958162000eba565b818152604036602083013763e462db9960e01b620008b38262000f5f565b5263bcc25ca960e01b620008c78262000f83565b5260405190620008d78262000eba565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031682526020820187905260408201526200091a8462000fe9565b52620009268362000fe9565b5060405190620009368262000eba565b81526040366020830137631249c58b60e01b620009538262000f5f565b5263c87b56dd60e01b620009678262000f83565b5260405190620009778262000eba565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168252602082018690526040820152620009ba8362000ffb565b52620009c68262000ffb565b506001600160a01b031690813b1562000c16578360405180926307e4c70760e21b8252606482016060600484015281518091526084830190602060848260051b86010193019185905b82821062000b66575050505060209083602484015283838203916003198301604486015252018183865af1801562000b5b5762000b45575b50803b1562000b3657604051632f539c4560e01b81526001600160801b03198316600482015260248035908201528390818160448183875af1801562000b3a5762000b1e575b50546801000000000000000081101562000b0a579262000ab584600160209601835562000e6f565b81546001600160a01b0360039290921b91821b19169084901b179055604051926001600160801b0319169082907fe3937c267d9e05e9cecc0f6f853600f165dac91585c676e0c75d229241f62d759080a38152f35b634e487b7160e01b84526041600452602484fd5b62000b299062000eed565b62000b3657823862000a8d565b8280fd5b6040513d84823e3d90fd5b62000b539093919362000eed565b913862000a47565b6040513d86823e3d90fd5b92955092909350608319868203018252845160018060a01b0381511682526020810151600381101562000c0257906040916020840152015190606060408201526020608060608301928451809452019201908a905b80821062000bde5750505060208060019296019201920192859389959362000a0f565b82516001600160e01b03191684526020938401939092019160019091019062000bbb565b634e487b7160e01b8b52602160045260248bfd5b8380fd5b60209060405162000c2b8162000eba565b8681528683820152606060408201528282860101520162000403565b634e487b7160e01b85526041600452602485fd5b6040513d85823e3d90fd5b634e487b7160e01b86526041600452602486fd5b60405162461bcd60e51b8152602060048201526015602482015274496e76616c69642069737375657220706172616d7360581b6044820152606490fd5b50602435151562000280565b5080fd5b5034620000dc5780600319360112620000dc576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620000dc5780600319360112620000dc576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620000dc5780600319360112620000dc576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620000dc5780600319360112620000dc576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620000dc5780600319360112620000dc576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b90503462000cc3578160031936011262000cc3577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b90600091825481101562000ea6578280527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563019190565b634e487b7160e01b83526032600452602483fd5b6060810190811067ffffffffffffffff82111762000ed757604052565b634e487b7160e01b600052604160045260246000fd5b67ffffffffffffffff811162000ed757604052565b6040810190811067ffffffffffffffff82111762000ed757604052565b6080810190811067ffffffffffffffff82111762000ed757604052565b90601f8019910116810190811067ffffffffffffffff82111762000ed757604052565b80511562000f6d5760200190565b634e487b7160e01b600052603260045260246000fd5b80516001101562000f6d5760400190565b80516002101562000f6d5760600190565b80516003101562000f6d5760800190565b80516004101562000f6d5760a00190565b80516005101562000f6d5760c00190565b80516006101562000f6d5760e00190565b80516007101562000f6d576101000190565b80516008101562000f6d576101200190565b919082519283825260005b8481106200103a575050826000602080949584010152601f8019910116010190565b60208183018101518483018201520162001018565b600091908291620010a3604051809262001083602083019563319af33360e01b87526040602485015260648401906200100d565b6001600160a01b0391909116604483015203601f19810183528262000f3c565b51906a636f6e736f6c652e6c6f675afa5056fe60803461072e576001600160401b0390610f3d90601f38839003908101601f191682019084821183831017610733578083916040958694855283398101031261072e5761004b81610787565b906100596020809201610787565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c132080546001600160a01b039485166001600160a01b031982168117909255919260009290919085167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08480a36100cd610768565b9260019081855282845b81811061070a5750506100e8610768565b8281528336818301376307e4c70760e21b6101028261079b565b528661010c610749565b921682528484830152878201526101228561079b565b5261012c8461079b565b50855196828801908111888210176106f65786528287529382855b610269575b5085519460608087019080885286518092526080938489019086868560051b8c010199019588935b8585106101c657508b7f8faa70878671ccd212d20771b795c50af8fd3ff6cf27f4bde57e5d4de0aeb6738c8f6101b78e83928f8f850152838203878501526107f7565b0390a15160ac9081610e318239f35b9091929394959699607f198c82030185528a5190828101918581511682528a8101516003811015610255578b8301528e0151818f018490528051928390528a019189918b91908601908d905b808210610231575050819293509c019501950193969594929190610174565b85516001600160e01b0319168352948301948c948e94939093019290910190610212565b634e487b7160e01b8d52602160045260248dfd5b84959195518110156106ee578261028082876107be565b51015160038110156106da57806104095750818661029e83886107be565b515116886102ac84896107be565b5101516102bb81511515610837565b6102c6821515610897565b6001600160a01b0382166000908152600080516020610f1d83398151915260205260409020546001600160601b031680156103fb575b9280949388915b61031e575b50505050610316905b6107d2565b909591610147565b909192939483518310156103f457506001600160e01b031961034083856107be565b5116808952600080516020610edd83398151915288528a8c8a20541661038a5761037b82610376878a9897969561038195610a4c565b6108f8565b916107d2565b90849594610303565b8b5162461bcd60e51b815260048101899052603560248201527f4c69624469616d6f6e644375743a2043616e2774206164642066756e6374696f60448201527f6e207468617420616c72656164792065786973747300000000000000000000006064820152608490fd5b9493610308565b61040483610937565b6102fc565b8083036105795750818661041d83886107be565b5151168861042b84896107be565b51015161043a81511515610837565b610445821515610897565b6001600160a01b0382166000908152600080516020610f1d83398151915260205260409020546001600160601b0316801561056b575b9280949388915b6104935750505050610316906107d2565b909192939483518310156103f457506001600160e01b03196104b583856107be565b5116808952600080516020610edd83398151915288528a8c8a2054168581146105015782610376878a98979695856104f36104f89761037b97610b37565b610a4c565b90849594610482565b8c5162461bcd60e51b8152600481018a9052603860248201527f4c69624469616d6f6e644375743a2043616e2774207265706c6163652066756e60448201527f6374696f6e20776974682073616d652066756e6374696f6e00000000000000006064820152608490fd5b61057483610937565b61047b565b600203610686578561058b82876107be565b5151168761059983886107be565b510151906105a982511515610837565b61061c5782919085835b6105c3575b5050610316906107d2565b81929391518110156106135761060a906103116001600160e01b03196105e983876107be565b5116808a52600080516020610edd83398151915289528b8d8b205416610b37565b819392916105b3565b819392506105b8565b875162461bcd60e51b815260048101859052603660248201527f4c69624469616d6f6e644375743a2052656d6f7665206661636574206164647260448201527f657373206d7573742062652061646472657373283029000000000000000000006064820152608490fd5b865162461bcd60e51b815260048101849052602760248201527f4c69624469616d6f6e644375743a20496e636f727265637420466163657443756044820152663a20b1ba34b7b760c91b6064820152608490fd5b634e487b7160e01b85526021600452602485fd5b94909461014c565b634e487b7160e01b84526041600452602484fd5b610712610749565b868152868382015260608a8201528282890101520183906100d7565b600080fd5b634e487b7160e01b600052604160045260246000fd5b60405190606082016001600160401b0381118382101761073357604052565b60408051919082016001600160401b0381118382101761073357604052565b51906001600160a01b038216820361072e57565b8051156107a85760200190565b634e487b7160e01b600052603260045260246000fd5b80518210156107a85760209160051b010190565b60001981146107e15760010190565b634e487b7160e01b600052601160045260246000fd5b919082519283825260005b848110610823575050826000602080949584010152601f8019910116010190565b602081830181015184830182015201610802565b1561083e57565b60405162461bcd60e51b815260206004820152602b60248201527f4c69624469616d6f6e644375743a204e6f2073656c6563746f727320696e206660448201526a1858d95d081d1bc818dd5d60aa1b6064820152608490fd5b1561089e57565b60405162461bcd60e51b815260206004820152602c60248201527f4c69624469616d6f6e644375743a204164642066616365742063616e2774206260448201526b65206164647265737328302960a01b6064820152608490fd5b6001600160601b039081169081146107e15760010190565b600080516020610efd83398151915280548210156107a85760005260206000200190600090565b61093f610749565b602481527f4c69624469616d6f6e644375743a204e657720666163657420686173206e6f20602082015263636f646560e01b6040820152813b15610a015750600080516020610efd83398151915280546001600160a01b0383166000908152600080516020610f1d8339815191526020526040902060010181905591906801000000000000000083101561073357826109e09160016109ff95019055610910565b90919082549060031b9160018060a01b03809116831b921b1916179055565b565b60405162461bcd60e51b815260206004820152908190610a259060248301906107f7565b0390fd5b91909180548310156107a857600052601c60206000208360031c019260021b1690565b6001600160e01b031981166000818152600080516020610edd83398151915260208190526040822080546001600160a01b031660a09690961b6001600160a01b031916959095179094559194939092906001600160a01b0316808352600080516020610f1d8339815191526020526040832080549194919068010000000000000000821015610b235796610aed8260409798996001610b0a95018155610a29565b90919063ffffffff83549160031b9260e01c831b921b1916179055565b82526020522080546001600160a01b0319169091179055565b634e487b7160e01b85526041600452602485fd5b9091906001600160a01b039081168015610dc557308114610d695763ffffffff60e01b809416600092818452600080516020610edd833981519152926020918483526040948587205460a01c90838852600080516020610f1d8339815191529586865287892054926000199b8c8501948511610d5557908991888c898c89808703610ce7575b505090525050508787525087892080548015610cd3578c0190610be08282610a29565b63ffffffff82549160031b1b191690555588528452868681205515610c0a575b5050505050509050565b600080516020610efd8339815191528054898101908111610cbf57838852858552826001888a20015491808303610c8d575b5050508054988915610c795760019798990191610c5883610910565b909182549160031b1b19169055558552528220015580388080808080610c00565b634e487b7160e01b88526031600452602488fd5b610c9690610910565b90549060031b1c16610cab816109e084610910565b885285855260018789200155388281610c3c565b634e487b7160e01b88526011600452602488fd5b634e487b7160e01b8b52603160045260248bfd5b610d489784610aed93610d068a9487610d1c9952828a52848420610a29565b90549060031b1c60e01b97889683525220610a29565b168b52838852898b2080546001600160a01b031660a09290921b6001600160a01b031916919091179055565b873880888c898c89610bbd565b634e487b7160e01b8b52601160045260248bfd5b60405162461bcd60e51b815260206004820152602e60248201527f4c69624469616d6f6e644375743a2043616e27742072656d6f766520696d6d7560448201526d3a30b1363290333ab731ba34b7b760911b6064820152608490fd5b60405162461bcd60e51b815260206004820152603760248201527f4c69624469616d6f6e644375743a2043616e27742072656d6f76652066756e6360448201527f74696f6e207468617420646f65736e27742065786973740000000000000000006064820152608490fdfe6080604052361560aa57600080356001600160e01b03191681527fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c60205260408120546001600160a01b03168015606c57818091368280378136915af43d82803e156068573d90f35b3d90fd5b62461bcd60e51b6080526020608452602060a4527f4469616d6f6e643a2046756e6374696f6e20646f6573206e6f7420657869737460c45260646080fd5b00c8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131cc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131ec8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131d", - "sourceMap": "884:6733:31:-:0;;;;;;;;;;;;-1:-1:-1;;884:6733:31;;;-1:-1:-1;;;;;884:6733:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;-1:-1:-1;;;;;884:6733:31;;1905:30;884:6733;;;2023:34;884:6733;2067:26;884:6733;2103:36;884:6733;2149:34;2193:24;;2227:38;;2275:50;;2335:32;;;;2377:28;;;;2415:42;;;;884:6733;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;884:6733:31;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;884:6733:31;;;;;;-1:-1:-1;884:6733:31;;;;;-1:-1:-1;884:6733:31;;;;-1:-1:-1;;;;;884:6733:31;;;;;;:::o", - "linkReferences": {} - }, - "deployedBytecode": { - "object": "0x60808060405260043610156200001457600080fd5b600090813560e01c90816305a5ca2a1462000e2a5750806308f91d7b1462000de357806316adefe11462000d9c57806332b882a11462000d555780633d4ed9f71462000d0e57806350896c431462000cc75780636adff8fb14620002435780637f8a200c14620001fc57806383d3052e14620001b5578063a051e9b3146200016e578063b1dbd67f1462000127578063ba98c55414620000df5763f0f6d5a014620000be57600080fd5b34620000dc5780600319360112620000dc5760209054604051908152f35b80fd5b5034620000dc576020366003190112620000dc57600435908054821015620000dc5760206200010e8362000e6f565b905460405160039290921b1c6001600160a01b03168152f35b5034620000dc5780600319360112620000dc576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620000dc5780600319360112620000dc576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620000dc5780600319360112620000dc576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620000dc5780600319360112620000dc576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620000dc576040366003190112620000dc576004356001600160801b03198116810362000cc3576001600160801b0319811615158062000cb7575b1562000c7a578180604051620002968162000f02565b6015815274696e73696465206372656174654361705461626c6560581b6020820152604051620002f481620002e5602082019463104c13eb60e21b86526020602484015260448301906200100d565b03601f19810183528262000f3c565b51906a636f6e736f6c652e6c6f675afa5062000338604051620003178162000f02565b600c81526b036b9b39739b2b73232b91d160a51b602082015233906200104f565b6200037c6040516200034a8162000f02565b601881527f666163746f72792061646472657373202874686973293a200000000000000000602082015230906200104f565b604051610f3d9081810167ffffffffffffffff928282108483111762000c66576040918391620010b783393081527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316602082015203019084f0801562000c5b576040519161014083019081118382101762000c475760405260098252835b610120811062000c1a57506040516200041c8162000eba565b6002908181526040366020830137632f539c4560e01b6200043d8262000f5f565b52633f7a08e360e01b620004518262000f83565b5260405190620004618262000eba565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168252602082018790526040820152620004a48462000f5f565b52620004b08362000f5f565b50604051620004bf8162000f1f565b600381526060366020830137639e35325f60e01b620004de8262000f5f565b5263eafa8a5f60e01b620004f28262000f83565b52631b5b3e0960e21b620005068262000f94565b5260405190620005168262000eba565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168252602082018790526040820152620005598462000f83565b52620005658362000f83565b50604051620005748162000eba565b818152604036602083013763257eac7d60e11b620005928262000f5f565b52630cfdbcc560e31b620005a68262000f83565b5260405190620005b68262000eba565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168252602082018790526040820152620005f98462000f94565b52620006058362000f94565b50604051620006148162000f02565b60018152602036818301376355356b8b60e01b620006328262000f5f565b5260405190620006428262000eba565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168252602082018790526040820152620006858462000fa5565b52620006918362000fa5565b50604051620006a08162000eba565b8181526040366020830137636eec76af60e11b620006be8262000f5f565b52631d3e944560e31b620006d28262000f83565b5260405190620006e28262000eba565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168252602082018790526040820152620007258462000fb6565b52620007318362000fb6565b50604051620007408162000f1f565b600381526060366020830137630d062fcb60e01b6200075f8262000f5f565b52630d08aa0f60e01b620007738262000f83565b526335af1e9160e11b620007878262000f94565b5260405190620007978262000eba565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168252602082018790526040820152620007da8462000fc7565b52620007e68362000fc7565b50604051620007f58162000eba565b8181526040366020830137636b348f4160e01b620008138262000f5f565b52633871654760e21b620008278262000f83565b5260405190620008378262000eba565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031682526020820187905260408201526200087a8462000fd8565b52620008868362000fd8565b50604051620008958162000eba565b818152604036602083013763e462db9960e01b620008b38262000f5f565b5263bcc25ca960e01b620008c78262000f83565b5260405190620008d78262000eba565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031682526020820187905260408201526200091a8462000fe9565b52620009268362000fe9565b5060405190620009368262000eba565b81526040366020830137631249c58b60e01b620009538262000f5f565b5263c87b56dd60e01b620009678262000f83565b5260405190620009778262000eba565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168252602082018690526040820152620009ba8362000ffb565b52620009c68262000ffb565b506001600160a01b031690813b1562000c16578360405180926307e4c70760e21b8252606482016060600484015281518091526084830190602060848260051b86010193019185905b82821062000b66575050505060209083602484015283838203916003198301604486015252018183865af1801562000b5b5762000b45575b50803b1562000b3657604051632f539c4560e01b81526001600160801b03198316600482015260248035908201528390818160448183875af1801562000b3a5762000b1e575b50546801000000000000000081101562000b0a579262000ab584600160209601835562000e6f565b81546001600160a01b0360039290921b91821b19169084901b179055604051926001600160801b0319169082907fe3937c267d9e05e9cecc0f6f853600f165dac91585c676e0c75d229241f62d759080a38152f35b634e487b7160e01b84526041600452602484fd5b62000b299062000eed565b62000b3657823862000a8d565b8280fd5b6040513d84823e3d90fd5b62000b539093919362000eed565b913862000a47565b6040513d86823e3d90fd5b92955092909350608319868203018252845160018060a01b0381511682526020810151600381101562000c0257906040916020840152015190606060408201526020608060608301928451809452019201908a905b80821062000bde5750505060208060019296019201920192859389959362000a0f565b82516001600160e01b03191684526020938401939092019160019091019062000bbb565b634e487b7160e01b8b52602160045260248bfd5b8380fd5b60209060405162000c2b8162000eba565b8681528683820152606060408201528282860101520162000403565b634e487b7160e01b85526041600452602485fd5b6040513d85823e3d90fd5b634e487b7160e01b86526041600452602486fd5b60405162461bcd60e51b8152602060048201526015602482015274496e76616c69642069737375657220706172616d7360581b6044820152606490fd5b50602435151562000280565b5080fd5b5034620000dc5780600319360112620000dc576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620000dc5780600319360112620000dc576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620000dc5780600319360112620000dc576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620000dc5780600319360112620000dc576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620000dc5780600319360112620000dc576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b90503462000cc3578160031936011262000cc3577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b90600091825481101562000ea6578280527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563019190565b634e487b7160e01b83526032600452602483fd5b6060810190811067ffffffffffffffff82111762000ed757604052565b634e487b7160e01b600052604160045260246000fd5b67ffffffffffffffff811162000ed757604052565b6040810190811067ffffffffffffffff82111762000ed757604052565b6080810190811067ffffffffffffffff82111762000ed757604052565b90601f8019910116810190811067ffffffffffffffff82111762000ed757604052565b80511562000f6d5760200190565b634e487b7160e01b600052603260045260246000fd5b80516001101562000f6d5760400190565b80516002101562000f6d5760600190565b80516003101562000f6d5760800190565b80516004101562000f6d5760a00190565b80516005101562000f6d5760c00190565b80516006101562000f6d5760e00190565b80516007101562000f6d576101000190565b80516008101562000f6d576101200190565b919082519283825260005b8481106200103a575050826000602080949584010152601f8019910116010190565b60208183018101518483018201520162001018565b600091908291620010a3604051809262001083602083019563319af33360e01b87526040602485015260648401906200100d565b6001600160a01b0391909116604483015203601f19810183528262000f3c565b51906a636f6e736f6c652e6c6f675afa5056fe60803461072e576001600160401b0390610f3d90601f38839003908101601f191682019084821183831017610733578083916040958694855283398101031261072e5761004b81610787565b906100596020809201610787565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c132080546001600160a01b039485166001600160a01b031982168117909255919260009290919085167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08480a36100cd610768565b9260019081855282845b81811061070a5750506100e8610768565b8281528336818301376307e4c70760e21b6101028261079b565b528661010c610749565b921682528484830152878201526101228561079b565b5261012c8461079b565b50855196828801908111888210176106f65786528287529382855b610269575b5085519460608087019080885286518092526080938489019086868560051b8c010199019588935b8585106101c657508b7f8faa70878671ccd212d20771b795c50af8fd3ff6cf27f4bde57e5d4de0aeb6738c8f6101b78e83928f8f850152838203878501526107f7565b0390a15160ac9081610e318239f35b9091929394959699607f198c82030185528a5190828101918581511682528a8101516003811015610255578b8301528e0151818f018490528051928390528a019189918b91908601908d905b808210610231575050819293509c019501950193969594929190610174565b85516001600160e01b0319168352948301948c948e94939093019290910190610212565b634e487b7160e01b8d52602160045260248dfd5b84959195518110156106ee578261028082876107be565b51015160038110156106da57806104095750818661029e83886107be565b515116886102ac84896107be565b5101516102bb81511515610837565b6102c6821515610897565b6001600160a01b0382166000908152600080516020610f1d83398151915260205260409020546001600160601b031680156103fb575b9280949388915b61031e575b50505050610316905b6107d2565b909591610147565b909192939483518310156103f457506001600160e01b031961034083856107be565b5116808952600080516020610edd83398151915288528a8c8a20541661038a5761037b82610376878a9897969561038195610a4c565b6108f8565b916107d2565b90849594610303565b8b5162461bcd60e51b815260048101899052603560248201527f4c69624469616d6f6e644375743a2043616e2774206164642066756e6374696f60448201527f6e207468617420616c72656164792065786973747300000000000000000000006064820152608490fd5b9493610308565b61040483610937565b6102fc565b8083036105795750818661041d83886107be565b5151168861042b84896107be565b51015161043a81511515610837565b610445821515610897565b6001600160a01b0382166000908152600080516020610f1d83398151915260205260409020546001600160601b0316801561056b575b9280949388915b6104935750505050610316906107d2565b909192939483518310156103f457506001600160e01b03196104b583856107be565b5116808952600080516020610edd83398151915288528a8c8a2054168581146105015782610376878a98979695856104f36104f89761037b97610b37565b610a4c565b90849594610482565b8c5162461bcd60e51b8152600481018a9052603860248201527f4c69624469616d6f6e644375743a2043616e2774207265706c6163652066756e60448201527f6374696f6e20776974682073616d652066756e6374696f6e00000000000000006064820152608490fd5b61057483610937565b61047b565b600203610686578561058b82876107be565b5151168761059983886107be565b510151906105a982511515610837565b61061c5782919085835b6105c3575b5050610316906107d2565b81929391518110156106135761060a906103116001600160e01b03196105e983876107be565b5116808a52600080516020610edd83398151915289528b8d8b205416610b37565b819392916105b3565b819392506105b8565b875162461bcd60e51b815260048101859052603660248201527f4c69624469616d6f6e644375743a2052656d6f7665206661636574206164647260448201527f657373206d7573742062652061646472657373283029000000000000000000006064820152608490fd5b865162461bcd60e51b815260048101849052602760248201527f4c69624469616d6f6e644375743a20496e636f727265637420466163657443756044820152663a20b1ba34b7b760c91b6064820152608490fd5b634e487b7160e01b85526021600452602485fd5b94909461014c565b634e487b7160e01b84526041600452602484fd5b610712610749565b868152868382015260608a8201528282890101520183906100d7565b600080fd5b634e487b7160e01b600052604160045260246000fd5b60405190606082016001600160401b0381118382101761073357604052565b60408051919082016001600160401b0381118382101761073357604052565b51906001600160a01b038216820361072e57565b8051156107a85760200190565b634e487b7160e01b600052603260045260246000fd5b80518210156107a85760209160051b010190565b60001981146107e15760010190565b634e487b7160e01b600052601160045260246000fd5b919082519283825260005b848110610823575050826000602080949584010152601f8019910116010190565b602081830181015184830182015201610802565b1561083e57565b60405162461bcd60e51b815260206004820152602b60248201527f4c69624469616d6f6e644375743a204e6f2073656c6563746f727320696e206660448201526a1858d95d081d1bc818dd5d60aa1b6064820152608490fd5b1561089e57565b60405162461bcd60e51b815260206004820152602c60248201527f4c69624469616d6f6e644375743a204164642066616365742063616e2774206260448201526b65206164647265737328302960a01b6064820152608490fd5b6001600160601b039081169081146107e15760010190565b600080516020610efd83398151915280548210156107a85760005260206000200190600090565b61093f610749565b602481527f4c69624469616d6f6e644375743a204e657720666163657420686173206e6f20602082015263636f646560e01b6040820152813b15610a015750600080516020610efd83398151915280546001600160a01b0383166000908152600080516020610f1d8339815191526020526040902060010181905591906801000000000000000083101561073357826109e09160016109ff95019055610910565b90919082549060031b9160018060a01b03809116831b921b1916179055565b565b60405162461bcd60e51b815260206004820152908190610a259060248301906107f7565b0390fd5b91909180548310156107a857600052601c60206000208360031c019260021b1690565b6001600160e01b031981166000818152600080516020610edd83398151915260208190526040822080546001600160a01b031660a09690961b6001600160a01b031916959095179094559194939092906001600160a01b0316808352600080516020610f1d8339815191526020526040832080549194919068010000000000000000821015610b235796610aed8260409798996001610b0a95018155610a29565b90919063ffffffff83549160031b9260e01c831b921b1916179055565b82526020522080546001600160a01b0319169091179055565b634e487b7160e01b85526041600452602485fd5b9091906001600160a01b039081168015610dc557308114610d695763ffffffff60e01b809416600092818452600080516020610edd833981519152926020918483526040948587205460a01c90838852600080516020610f1d8339815191529586865287892054926000199b8c8501948511610d5557908991888c898c89808703610ce7575b505090525050508787525087892080548015610cd3578c0190610be08282610a29565b63ffffffff82549160031b1b191690555588528452868681205515610c0a575b5050505050509050565b600080516020610efd8339815191528054898101908111610cbf57838852858552826001888a20015491808303610c8d575b5050508054988915610c795760019798990191610c5883610910565b909182549160031b1b19169055558552528220015580388080808080610c00565b634e487b7160e01b88526031600452602488fd5b610c9690610910565b90549060031b1c16610cab816109e084610910565b885285855260018789200155388281610c3c565b634e487b7160e01b88526011600452602488fd5b634e487b7160e01b8b52603160045260248bfd5b610d489784610aed93610d068a9487610d1c9952828a52848420610a29565b90549060031b1c60e01b97889683525220610a29565b168b52838852898b2080546001600160a01b031660a09290921b6001600160a01b031916919091179055565b873880888c898c89610bbd565b634e487b7160e01b8b52601160045260248bfd5b60405162461bcd60e51b815260206004820152602e60248201527f4c69624469616d6f6e644375743a2043616e27742072656d6f766520696d6d7560448201526d3a30b1363290333ab731ba34b7b760911b6064820152608490fd5b60405162461bcd60e51b815260206004820152603760248201527f4c69624469616d6f6e644375743a2043616e27742072656d6f76652066756e6360448201527f74696f6e207468617420646f65736e27742065786973740000000000000000006064820152608490fdfe6080604052361560aa57600080356001600160e01b03191681527fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c60205260408120546001600160a01b03168015606c57818091368280378136915af43d82803e156068573d90f35b3d90fd5b62461bcd60e51b6080526020608452602060a4527f4469616d6f6e643a2046756e6374696f6e20646f6573206e6f7420657869737460c45260646080fd5b00c8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131cc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131ec8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131d", - "sourceMap": "884:6733:31:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;884:6733:31;;;;;;;;;1002:26;;;;;884:6733;1002:26;;;:::i;:::-;884:6733;;;;;;;;;;-1:-1:-1;;;;;884:6733:31;;;;;;;;;;;;;;;;;;;1433:37;-1:-1:-1;;;;;884:6733:31;;;;;;;;;;;;;;;;;;;;;1064:40;-1:-1:-1;;;;;884:6733:31;;;;;;;;;;;;;;;;;;;;;1199:40;-1:-1:-1;;;;;884:6733:31;;;;;;;;;;;;;;;;;;;;;1110:36;-1:-1:-1;;;;;884:6733:31;;;;;;;;;;;;;-1:-1:-1;;884:6733:31;;;;;;-1:-1:-1;;;;;;884:6733:31;;;;;;-1:-1:-1;;;;;;884:6733:31;;2584:16;;;:48;;884:6733;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;884:6733:31;;;;;;5701:42:13;;884:6733:31;;5701:42:13;;;;;;;;884:6733:31;;5701:42:13;;884:6733:31;;;;;;:::i;:::-;5701:42:13;;;;;;;;;:::i;:::-;884:6733:31;392:159:13;131:42;392:159;;;2767:39:31;884:6733;;;;;:::i;:::-;;;;-1:-1:-1;;;884:6733:31;;;;2795:10;2767:39;;:::i;:::-;2816:54;884:6733;;;;;:::i;:::-;;;;;;;;;2864:4;2816:54;;:::i;:::-;884:6733;;2961:51;;;;;;;;;;;;;;;;884:6733;2961:51;;;;;;2864:4;884:6733;;2996:15;-1:-1:-1;;;;;884:6733:31;;;;;2961:51;;;;;;;;;884:6733;;;;;;;;;;;;;;;;;3126:1;884:6733;;;;;;;;;;;;;;;:::i;:::-;3209:1;884:6733;;;;;;;;;;-1:-1:-1;;;3221:58:31;;;:::i;:::-;884:6733;-1:-1:-1;;;3289:70:31;;;:::i;:::-;884:6733;;;;;;;:::i;:::-;3416:11;-1:-1:-1;;;;;884:6733:31;;;;3379:127;;884:6733;;;;3379:127;;884:6733;3369:137;;;:::i;:::-;;;;;:::i;:::-;;884:6733;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;;;3609:69:31;;;:::i;:::-;884:6733;-1:-1:-1;;;3688:74:31;;;:::i;:::-;884:6733;-1:-1:-1;;;3772:75:31;;;:::i;:::-;884:6733;;;;;;;:::i;:::-;3916:16;-1:-1:-1;;;;;884:6733:31;;;;3867:181;;884:6733;;;;3867:181;;884:6733;3857:191;;;:::i;:::-;;;;;:::i;:::-;;884:6733;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;;;4149:66:31;;;:::i;:::-;884:6733;-1:-1:-1;;;4225:72:31;;;:::i;:::-;884:6733;;;;;;;:::i;:::-;4366:15;-1:-1:-1;;;;;884:6733:31;;;;4317:179;;884:6733;;;;4317:179;;884:6733;4307:189;;;:::i;:::-;;;;;:::i;:::-;;884:6733;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;;;4587:50:31;;;:::i;:::-;884:6733;;;;;;;:::i;:::-;4694:10;-1:-1:-1;;;;;884:6733:31;;;;4657:125;;884:6733;;;;4657:125;;884:6733;4647:135;;;:::i;:::-;;;;;:::i;:::-;;884:6733;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;;;4886:69:31;;;:::i;:::-;884:6733;-1:-1:-1;;;4965:75:31;;;:::i;:::-;884:6733;;;;;;;:::i;:::-;5109:17;-1:-1:-1;;;;;884:6733:31;;;;5060:182;;884:6733;;;;5060:182;;884:6733;5050:192;;;:::i;:::-;;;;;:::i;:::-;;884:6733;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;;;5359:89:31;;;:::i;:::-;884:6733;-1:-1:-1;;;5458:77:31;;;:::i;:::-;884:6733;-1:-1:-1;;;5545:92:31;;;:::i;:::-;884:6733;;;;;;;:::i;:::-;5706:23;-1:-1:-1;;;;;884:6733:31;;;;5657:195;;884:6733;;;;5657:195;;884:6733;5647:205;;;:::i;:::-;;;;;:::i;:::-;;884:6733;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;;;5951:63:31;;;:::i;:::-;884:6733;-1:-1:-1;;;6024:67:31;;;:::i;:::-;884:6733;;;;;;;:::i;:::-;6160:14;-1:-1:-1;;;;;884:6733:31;;;;6111:177;;884:6733;;;;6111:177;;884:6733;6101:187;;;:::i;:::-;;;;;:::i;:::-;;884:6733;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;;;6383:56:31;;;:::i;:::-;884:6733;-1:-1:-1;;;6449:62:31;;;:::i;:::-;884:6733;;;;;;;:::i;:::-;6568:12;-1:-1:-1;;;;;884:6733:31;;;;6531:129;;884:6733;;;;6531:129;;884:6733;6521:139;;;:::i;:::-;;;;;:::i;:::-;;884:6733;;;;;;:::i;:::-;;;;;;;;;-1:-1:-1;;;6769:62:31;;;:::i;:::-;884:6733;-1:-1:-1;;;6841:66:31;;;:::i;:::-;884:6733;;;;;;;:::i;:::-;6976:19;-1:-1:-1;;;;;884:6733:31;;;;6927:187;;884:6733;;;;6927:187;;884:6733;6917:197;;;:::i;:::-;;;;;:::i;:::-;-1:-1:-1;;;;;;884:6733:31;;7153:66;;;;;884:6733;;;;;;;;7153:66;;884:6733;;;;;7153:66;;884:6733;;;;;;;;;;;;;5652:1;884:6733;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7153:66;;;;;;;;;;;;884:6733;7263:75;;;;;;884:6733;;-1:-1:-1;;;7263:75:31;;-1:-1:-1;;;;;;884:6733:31;;;7263:75;;884:6733;;;;;;;;;;;;;;;7263:75;;;;;;;;;884:6733;;;;;;;;;;;;;;;;;;;:::i;:::-;;;-1:-1:-1;;;;;884:6733:31;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;884:6733:31;;;;7432:37;;;;884:6733;;;;-1:-1:-1;;;884:6733:31;;;;;;;;7263:75;;;;:::i;:::-;884:6733;;7263:75;;;;884:6733;;;;7263:75;884:6733;;;;;;;;;7153:66;;;;;;;:::i;:::-;;;;;;884:6733;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;884:6733:31;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;884:6733:31;;;;;;;;7153:66;884:6733;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;884:6733:31;;;;;;;;2961:51;884:6733;;;;;;;;;2961:51;-1:-1:-1;;;884:6733:31;;;;;;;;;;;-1:-1:-1;;;884:6733:31;;;;;;;;;;;;-1:-1:-1;;;884:6733:31;;;;;;;2584:48;884:6733;;;2604:28;;2584:48;;884:6733;;;;;;;;;;;;;;;;;;;1152:41;-1:-1:-1;;;;;884:6733:31;;;;;;;;;;;;;;;;;;;;;1334:48;-1:-1:-1;;;;;884:6733:31;;;;;;;;;;;;;;;;;;;;;1286:42;-1:-1:-1;;;;;884:6733:31;;;;;;;;;;;;;;;;;;;;;1388:39;-1:-1:-1;;;;;884:6733:31;;;;;;;;;;;;;;;;;;;;;1476:44;-1:-1:-1;;;;;884:6733:31;;;;;;;;;;;;;;;;;;;;1245:35;-1:-1:-1;;;;;884:6733:31;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;-1:-1:-1;;;884:6733:31;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;5701:42:13;;;884:6733:31;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;3305:1;884:6733;;;;;;;:::o;:::-;;;3209:1;884:6733;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;5652:1;884:6733;;;;;;;:::o;:::-;;;6106:1;884:6733;;;;;;;:::o;:::-;;;6526:1;884:6733;;;;;;;:::o;:::-;;;6922:1;884:6733;;;;;;;:::o;:::-;;;;;;;;;-1:-1:-1;884:6733:31;;;;;;;;;-1:-1:-1;884:6733:31;;;;;;;;5701:42:13;;;884:6733:31;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;6994:145:13;-1:-1:-1;6994:145:13;;;;7077:54;884:6733:31;;7077:54:13;;884:6733:31;7077:54:13;;;;;;;;;884:6733:31;7077:54:13;;;884:6733:31;;;;;;:::i;:::-;-1:-1:-1;;;;;884:6733:31;;;;;;;;7077:54:13;-1:-1:-1;;7077:54:13;;;;;;:::i;:::-;884:6733:31;392:159:13;131:42;392:159;;;6994:145::o", - "linkReferences": {}, - "immutableReferences": { - "28521": [ - { - "start": 390, - "length": 32 - }, - { - "start": 940, - "length": 32 - } - ], - "28523": [ - { - "start": 532, - "length": 32 - }, - { - "start": 1123, - "length": 32 - } - ], - "28525": [ - { - "start": 1304, - "length": 32 - }, - { - "start": 3295, - "length": 32 - } - ], - "28527": [ - { - "start": 461, - "length": 32 - }, - { - "start": 1464, - "length": 32 - } - ], - "28529": [ - { - "start": 1604, - "length": 32 - }, - { - "start": 3648, - "length": 32 - } - ], - "28531": [ - { - "start": 1764, - "length": 32 - }, - { - "start": 3437, - "length": 32 - } - ], - "28533": [ - { - "start": 1945, - "length": 32 - }, - { - "start": 3366, - "length": 32 - } - ], - "28535": [ - { - "start": 2105, - "length": 32 - }, - { - "start": 3508, - "length": 32 - } - ], - "28537": [ - { - "start": 319, - "length": 32 - }, - { - "start": 2265, - "length": 32 - } - ], - "28539": [ - { - "start": 2425, - "length": 32 - }, - { - "start": 3579, - "length": 32 - } - ] - } - }, - "methodIdentifiers": { - "capTables(uint256)": "ba98c554", - "convertiblesFacet()": "32b882a1", - "createCapTable(bytes16,uint256)": "6adff8fb", - "diamondCutFacet()": "a051e9b3", - "equityCompensationFacet()": "3d4ed9f7", - "getCapTableCount()": "f0f6d5a0", - "issuerFacet()": "7f8a200c", - "stakeholderFacet()": "50896c43", - "stakeholderNFTFacet()": "08f91d7b", - "stockClassFacet()": "83d3052e", - "stockFacet()": "05a5ca2a", - "stockPlanFacet()": "16adefe1", - "warrantFacet()": "b1dbd67f" - }, - "rawMetadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_diamondCutFacet\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_issuerFacet\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_stakeholderFacet\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_stockClassFacet\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_stockFacet\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_convertiblesFacet\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_equityCompensationFacet\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_stockPlanFacet\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_warrantFacet\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_stakeholderNFTFacet\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"capTable\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes16\",\"name\":\"issuerId\",\"type\":\"bytes16\"}],\"name\":\"CapTableCreated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"capTables\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"convertiblesFacet\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"id\",\"type\":\"bytes16\"},{\"internalType\":\"uint256\",\"name\":\"initialSharesAuthorized\",\"type\":\"uint256\"}],\"name\":\"createCapTable\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"diamondCutFacet\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"equityCompensationFacet\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCapTableCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"issuerFacet\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stakeholderFacet\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stakeholderNFTFacet\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stockClassFacet\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stockFacet\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"stockPlanFacet\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"warrantFacet\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/lib/diamond/DiamondCapTableFactory.sol\":\"DiamondCapTableFactory\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"appendCBOR\":false,\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@diamond/=src/lib/diamond/\",\":diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/\",\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\",\":openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/\"],\"viaIR\":true},\"sources\":{\"lib/diamond-3-hardhat/contracts/Diamond.sol\":{\"keccak256\":\"0x67a461e00a275fc809f4acdca15faed81204d08e11b455df489c7087b5d1f59c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://18e9ae1ec7ed300fd9c4dc6d7de8086a0f0f435549adf808cd0da8df9a460fef\",\"dweb:/ipfs/QmRdXCwZeU7Cw5srxJdTVKHUt7vrb6atyghJ11gBQLwYFv\"]},\"lib/diamond-3-hardhat/contracts/facets/DiamondCutFacet.sol\":{\"keccak256\":\"0x862aadba5321ffaf88456deadba8df4ec681e4a93ce3dc44d6c0c0b3b68a8230\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6b0dd3cf2631d2b1d070d8c5b24fc087853076b895d0c207386545463414efc7\",\"dweb:/ipfs/QmQuA6PvDjbHufjtDjrcuuxj8HBaABYyaRwD2Q9HyCWTV8\"]},\"lib/diamond-3-hardhat/contracts/interfaces/IDiamondCut.sol\":{\"keccak256\":\"0xc00c16bfa30a3fa5f3dc684f7f8ba62c259962b25f647d9588739458989717fc\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://119d9a5acd99b9462a8341c9b95ddd468648799eefa47038f81521431743c1ae\",\"dweb:/ipfs/QmTF7WNyPWTUtUzNcpq5rf5v2uw5TwzqsSg9D53pfQufcu\"]},\"lib/diamond-3-hardhat/contracts/libraries/LibDiamond.sol\":{\"keccak256\":\"0xc5044f5a7a031e4e1869a26addf83b25c8b20d5949ba13b613dfbc72ad2f63b0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://25f2ac88867d97bfd13b503c06512b082ecf861f3f00702ca2747502c9113a79\",\"dweb:/ipfs/QmSYDNeaLGXDsPa2maoaeim5qJiLNuM3PbDguoYmsUmAZL\"]},\"lib/forge-std/src/console.sol\":{\"keccak256\":\"0x91d5413c2434ca58fd278b6e1e79fd98d10c83931cc2596a6038eee4daeb34ba\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://91ccea707361e48b9b7a161fe81f496b9932bc471e9c4e4e1e9c283f2453cc70\",\"dweb:/ipfs/QmcB66sZhQ6Kz7MUHcLE78YXRUZxoZnnxZjN6yATsbB2ec\"]},\"lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol\":{\"keccak256\":\"0x2c309e7df9e05e6ce15bedfe74f3c61b467fc37e0fae9eab496acf5ea0bbd7ff\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7063b5c98711a98018ba4635ac74cee1c1cfa2ea01099498e062699ed9530005\",\"dweb:/ipfs/QmeJ8rGXkcv7RrqLdAW8PCXPAykxVsddfYY6g5NaTwmRFE\"]},\"lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol\":{\"keccak256\":\"0x5bce51e11f7d194b79ea59fe00c9e8de9fa2c5530124960f29a24d4c740a3266\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7e66dfde185df46104c11bc89d08fa0760737aa59a2b8546a656473d810a8ea4\",\"dweb:/ipfs/QmXvyqtXPaPss2PD7eqPoSao5Szm2n6UMoiG8TZZDjmChR\"]},\"lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol\":{\"keccak256\":\"0xa82b58eca1ee256be466e536706850163d2ec7821945abd6b4778cfb3bee37da\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6e75cf83beb757b8855791088546b8337e9d4684e169400c20d44a515353b708\",\"dweb:/ipfs/QmYvPafLfoquiDMEj7CKHtvbgHu7TJNPSVPSCjrtjV8HjV\"]},\"lib/openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Metadata.sol\":{\"keccak256\":\"0x75b829ff2f26c14355d1cba20e16fe7b29ca58eb5fef665ede48bc0f9c6c74b9\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://a0a107160525724f9e1bbbab031defc2f298296dd9e331f16a6f7130cec32146\",\"dweb:/ipfs/QmemujxSd7gX8A9M8UwmNbz4Ms3U9FG9QfudUgxwvTmPWf\"]},\"lib/openzeppelin-contracts/contracts/utils/Address.sol\":{\"keccak256\":\"0x006dd67219697fe68d7fbfdea512e7c4cb64a43565ed86171d67e844982da6fa\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://2455248c8ddd9cc6a7af76a13973cddf222072427e7b0e2a7d1aff345145e931\",\"dweb:/ipfs/QmfYjnjRbWqYpuxurqveE6HtzsY1Xx323J428AKQgtBJZm\"]},\"lib/openzeppelin-contracts/contracts/utils/Base64.sol\":{\"keccak256\":\"0x5f3461639fe20794cfb4db4a6d8477388a15b2e70a018043084b7c4bedfa8136\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://77e5309e2cc4cdc3395214edb0ff43ff5a5f7373f5a425383e540f6fab530f96\",\"dweb:/ipfs/QmTV8DZ9knJDa3b5NPBFQqjvTzodyZVjRUg5mx5A99JPLJ\"]},\"lib/openzeppelin-contracts/contracts/utils/Context.sol\":{\"keccak256\":\"0xe2e337e6dde9ef6b680e07338c493ebea1b5fd09b43424112868e9cc1706bca7\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6df0ddf21ce9f58271bdfaa85cde98b200ef242a05a3f85c2bc10a8294800a92\",\"dweb:/ipfs/QmRK2Y5Yc6BK7tGKkgsgn3aJEQGi5aakeSPZvS65PV8Xp3\"]},\"lib/openzeppelin-contracts/contracts/utils/Strings.sol\":{\"keccak256\":\"0x3088eb2868e8d13d89d16670b5f8612c4ab9ff8956272837d8e90106c59c14a0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://b81d9ff6559ea5c47fc573e17ece6d9ba5d6839e213e6ebc3b4c5c8fe4199d7f\",\"dweb:/ipfs/QmPCW1bFisUzJkyjroY3yipwfism9RRCigCcK1hbXtVM8n\"]},\"lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol\":{\"keccak256\":\"0xd10975de010d89fd1c78dc5e8a9a7e7f496198085c151648f20cba166b32582b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://fb0048dee081f6fffa5f74afc3fb328483c2a30504e94a0ddd2a5114d731ec4d\",\"dweb:/ipfs/QmZptt1nmYoA5SgjwnSgWqgUSDgm4q52Yos3xhnMv3MV43\"]},\"lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol\":{\"keccak256\":\"0x447a5f3ddc18419d41ff92b3773fb86471b1db25773e07f877f548918a185bf1\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://be161e54f24e5c6fae81a12db1a8ae87bc5ae1b0ddc805d82a1440a68455088f\",\"dweb:/ipfs/QmP7C3CHdY9urF4dEMb9wmsp1wMxHF6nhA2yQE5SKiPAdy\"]},\"lib/openzeppelin-contracts/contracts/utils/math/Math.sol\":{\"keccak256\":\"0xe4455ac1eb7fc497bb7402579e7b4d64d928b846fce7d2b6fde06d366f21c2b3\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://cc8841b3cd48ad125e2f46323c8bad3aa0e88e399ec62acb9e57efa7e7c8058c\",\"dweb:/ipfs/QmSqE4mXHA2BXW58deDbXE8MTcsL5JSKNDbm23sVQxRLPS\"]},\"lib/openzeppelin-contracts/contracts/utils/math/SignedMath.sol\":{\"keccak256\":\"0xf92515413956f529d95977adc9b0567d583c6203fc31ab1c23824c35187e3ddc\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://c50fcc459e49a9858b6d8ad5f911295cb7c9ab57567845a250bf0153f84a95c7\",\"dweb:/ipfs/QmcEW85JRzvDkQggxiBBLVAasXWdkhEysqypj9EaB6H2g6\"]},\"src/lib/Structs.sol\":{\"keccak256\":\"0x404e740ae677baf5cc57884ee32d9accb367ae58f56a23f4e59b4f2987ae5100\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://349ec5d7e23684f71836001d70c012e59c0f2c94c9fa1fae83f85eeccda1fe52\",\"dweb:/ipfs/QmTVT5hzGePT8FxFxWhX77bH1DS1Z8iu8NAfAdn4PfsyCh\"]},\"src/lib/diamond/DiamondCapTable.sol\":{\"keccak256\":\"0x3d21aa24845831867536b58226232fe91be363dc009b01bfd78ceb6d3700f368\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://87a8cb5e98e672612777ff542bcf8c6001a110f81f35ff9a90a645cef43da119\",\"dweb:/ipfs/Qme7fbxJLQqA76oiDuEJEZeGfNdvBKzGoE9qb6hapWfLhR\"]},\"src/lib/diamond/DiamondCapTableFactory.sol\":{\"keccak256\":\"0xc51a98284ea2222c7048958da187f189a736cbd03c38b3eb63cb9e5e4a3f99c7\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d9c3d613c7e74301be5a983eec00a07da919cf8e9279a327fcd3d0950c65c59c\",\"dweb:/ipfs/QmetwDfVFX4krxH2FtBmbS1Aq1phWHwwUYxaGtUyxo9vnR\"]},\"src/lib/diamond/DiamondTxHelper.sol\":{\"keccak256\":\"0xe72ba093a5b16735d0193624d5b6585deb2cfe7e419a468c90fd70317fccf5e6\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://4ae3bd1ccbe005c63bed07e255c2b25c9bfdb37b9d85bb75f8f65cfeb0f6df98\",\"dweb:/ipfs/QmbTpzVeLkvthzpasQLvuvAfwE3g4sMsjEfpRBNwyG4eMQ\"]},\"src/lib/diamond/Storage.sol\":{\"keccak256\":\"0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d\",\"dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL\"]},\"src/lib/diamond/Structs.sol\":{\"keccak256\":\"0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab\",\"dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD\"]},\"src/lib/diamond/facets/ConvertiblesFacet.sol\":{\"keccak256\":\"0x571d0b98da033ca9b1726bbc2dbe293c906a03fbc295b5a03a90ce173c1a723f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6402bd044e8c8a738c0f932609c2f0e16eb1eeab58340c6c4b512f773d72e3e7\",\"dweb:/ipfs/QmZgJCKhjnsCFM8kvVPGxtzUrk7atjA2CLVi6Cbpf867Ec\"]},\"src/lib/diamond/facets/EquityCompensationFacet.sol\":{\"keccak256\":\"0x12a4fc4b6b5cacb5b1acccc6b8405007e16c3a0f16e639907d6ec16e83780541\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://f8dc83374614eefb44872b3bc693dae0d2943e8d3e5840c055ffddea036d4fb6\",\"dweb:/ipfs/QmfCah76CqqRcY1FucVNa6MH5wBcnh1DwyCdq4bojb2YFe\"]},\"src/lib/diamond/facets/IssuerFacet.sol\":{\"keccak256\":\"0x7834ab041c438bbc3e641d1b9f30d0bcff7a3e4e120d3251094c6d9ddba4d200\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://c15e372f5e4605a7e8ec927937c45fe08f26f18592c63f25dbdc9eb744285005\",\"dweb:/ipfs/QmQunUVABRsZkEzyct1rYxuBqavr9fR4NkpqPhZAFvouab\"]},\"src/lib/diamond/facets/StakeholderFacet.sol\":{\"keccak256\":\"0x1d636a399b0bcbe8fa4115b6bd13b09cbc4dfaf73ce862a98419100ddeee56e4\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5748c73c6a7422af0b198ea6c150d21ce5a77413292f55c68543f4a8da8255b9\",\"dweb:/ipfs/Qma642YoBMLAdmxPKhks2ki8j6UmCQiH2vyQirJaHXz5eZ\"]},\"src/lib/diamond/facets/StakeholderNFTFacet.sol\":{\"keccak256\":\"0x4f73f0bf6b9c2ed639ae81e84f133283dd08e2c97dae5aa2d73e4d69fbee28c6\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://c7226634490391eb3b4919234292d9a62de8fc0d5a8ae53da0541ed594d4347d\",\"dweb:/ipfs/QmRoUEwX3V868EyiYqaW9ekHgH9kAH2Vpkgmu8SSpatHMR\"]},\"src/lib/diamond/facets/StockClassFacet.sol\":{\"keccak256\":\"0x3748ae9ad8b1038e75af76911a9e41472428685da93aa0ed754ac7755741305f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7b930f82dfa1901b5943fc1a2df406b2ccb52da5e7526f2d9a71beb08c562d13\",\"dweb:/ipfs/QmRDeck7qdyqeHR5BMBzHfv32jkjG51FHTmgjzgs7Q2NHX\"]},\"src/lib/diamond/facets/StockFacet.sol\":{\"keccak256\":\"0xc6765b22e33ccd04c7c5f65cd2a33a5eb18c9199be9ff941784a3f458445161a\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://99eb3c365628ca6a37b9851373d7570ed3cafa9a43f0809754bedbcebcb0b200\",\"dweb:/ipfs/QmQE93qCuuWdWZahUCrtEuAWDbKwUKRQC1726yqUPN9WCi\"]},\"src/lib/diamond/facets/StockPlanFacet.sol\":{\"keccak256\":\"0x2f7dae680ad926c2788ce52e65cdb95b31e323c1e04f4e69d304e68e81e4e727\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6d2de2814568867928c4340841625dbd2b0094bc224b1488f03f9799b949fe28\",\"dweb:/ipfs/QmU58fHpqy3Bp79ZoshMu6Rewp6PZTzrH8hmCERmy1u35o\"]},\"src/lib/diamond/facets/WarrantFacet.sol\":{\"keccak256\":\"0xf31df59fc2568e1b76c2babcfc2621a425cc83090e7ed0daa773ef4a7bac8773\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://b3403064921f8c7ef4cd3b8eb3bf58587bc8c78da0237f58e90081da406080c4\",\"dweb:/ipfs/QmYPud1rJR5jzhY19cPor2oPtVm3B3JybbbQBmtxXpjwmw\"]},\"src/lib/diamond/libraries/ValidationLib.sol\":{\"keccak256\":\"0xfcd772b0670acded810468d772729715e3cd4db25c02a0da77c7445903e1cc5f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d572672df16392825ed86c32a53453247f62b481787db0498993f857623e1ee6\",\"dweb:/ipfs/Qmbpoa5EZ7yRcB1mZkmb5nhfxhhFY9g8tMJxPf9ZrzfNfL\"]}},\"version\":1}", - "metadata": { - "compiler": { - "version": "0.8.20+commit.a1b79de6" - }, - "language": "Solidity", - "output": { - "abi": [ - { - "inputs": [ - { - "internalType": "address", - "name": "_diamondCutFacet", - "type": "address" - }, - { - "internalType": "address", - "name": "_issuerFacet", - "type": "address" - }, - { - "internalType": "address", - "name": "_stakeholderFacet", - "type": "address" - }, - { - "internalType": "address", - "name": "_stockClassFacet", - "type": "address" - }, - { - "internalType": "address", - "name": "_stockFacet", - "type": "address" - }, - { - "internalType": "address", - "name": "_convertiblesFacet", - "type": "address" - }, - { - "internalType": "address", - "name": "_equityCompensationFacet", - "type": "address" - }, - { - "internalType": "address", - "name": "_stockPlanFacet", - "type": "address" - }, - { - "internalType": "address", - "name": "_warrantFacet", - "type": "address" - }, - { - "internalType": "address", - "name": "_stakeholderNFTFacet", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "capTable", - "type": "address", - "indexed": true - }, - { - "internalType": "bytes16", - "name": "issuerId", - "type": "bytes16", - "indexed": true - } - ], - "type": "event", - "name": "CapTableCreated", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function", - "name": "capTables", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ] - }, - { - "inputs": [], - "stateMutability": "view", - "type": "function", - "name": "convertiblesFacet", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ] - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "id", - "type": "bytes16" - }, - { - "internalType": "uint256", - "name": "initialSharesAuthorized", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "createCapTable", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ] - }, - { - "inputs": [], - "stateMutability": "view", - "type": "function", - "name": "diamondCutFacet", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ] - }, - { - "inputs": [], - "stateMutability": "view", - "type": "function", - "name": "equityCompensationFacet", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ] - }, - { - "inputs": [], - "stateMutability": "view", - "type": "function", - "name": "getCapTableCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ] - }, - { - "inputs": [], - "stateMutability": "view", - "type": "function", - "name": "issuerFacet", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ] - }, - { - "inputs": [], - "stateMutability": "view", - "type": "function", - "name": "stakeholderFacet", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ] - }, - { - "inputs": [], - "stateMutability": "view", - "type": "function", - "name": "stakeholderNFTFacet", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ] - }, - { - "inputs": [], - "stateMutability": "view", - "type": "function", - "name": "stockClassFacet", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ] - }, - { - "inputs": [], - "stateMutability": "view", - "type": "function", - "name": "stockFacet", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ] - }, - { - "inputs": [], - "stateMutability": "view", - "type": "function", - "name": "stockPlanFacet", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ] - }, - { - "inputs": [], - "stateMutability": "view", - "type": "function", - "name": "warrantFacet", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ] - } - ], - "devdoc": { - "kind": "dev", - "methods": {}, - "version": 1 - }, - "userdoc": { - "kind": "user", - "methods": {}, - "version": 1 - } - }, - "settings": { - "remappings": [ - "@diamond/=src/lib/diamond/", - "diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/", - "ds-test/=lib/forge-std/lib/ds-test/src/", - "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", - "forge-std/=lib/forge-std/src/", - "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", - "openzeppelin-contracts/=lib/openzeppelin-contracts/", - "openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/" - ], - "optimizer": { - "enabled": true, - "runs": 200 - }, - "metadata": { - "bytecodeHash": "none", - "appendCBOR": false - }, - "compilationTarget": { - "src/lib/diamond/DiamondCapTableFactory.sol": "DiamondCapTableFactory" - }, - "evmVersion": "paris", - "libraries": {}, - "viaIR": true - }, - "sources": { - "lib/diamond-3-hardhat/contracts/Diamond.sol": { - "keccak256": "0x67a461e00a275fc809f4acdca15faed81204d08e11b455df489c7087b5d1f59c", - "urls": [ - "bzz-raw://18e9ae1ec7ed300fd9c4dc6d7de8086a0f0f435549adf808cd0da8df9a460fef", - "dweb:/ipfs/QmRdXCwZeU7Cw5srxJdTVKHUt7vrb6atyghJ11gBQLwYFv" - ], - "license": "MIT" - }, - "lib/diamond-3-hardhat/contracts/facets/DiamondCutFacet.sol": { - "keccak256": "0x862aadba5321ffaf88456deadba8df4ec681e4a93ce3dc44d6c0c0b3b68a8230", - "urls": [ - "bzz-raw://6b0dd3cf2631d2b1d070d8c5b24fc087853076b895d0c207386545463414efc7", - "dweb:/ipfs/QmQuA6PvDjbHufjtDjrcuuxj8HBaABYyaRwD2Q9HyCWTV8" - ], - "license": "MIT" - }, - "lib/diamond-3-hardhat/contracts/interfaces/IDiamondCut.sol": { - "keccak256": "0xc00c16bfa30a3fa5f3dc684f7f8ba62c259962b25f647d9588739458989717fc", - "urls": [ - "bzz-raw://119d9a5acd99b9462a8341c9b95ddd468648799eefa47038f81521431743c1ae", - "dweb:/ipfs/QmTF7WNyPWTUtUzNcpq5rf5v2uw5TwzqsSg9D53pfQufcu" - ], - "license": "MIT" - }, - "lib/diamond-3-hardhat/contracts/libraries/LibDiamond.sol": { - "keccak256": "0xc5044f5a7a031e4e1869a26addf83b25c8b20d5949ba13b613dfbc72ad2f63b0", - "urls": [ - "bzz-raw://25f2ac88867d97bfd13b503c06512b082ecf861f3f00702ca2747502c9113a79", - "dweb:/ipfs/QmSYDNeaLGXDsPa2maoaeim5qJiLNuM3PbDguoYmsUmAZL" - ], - "license": "MIT" - }, - "lib/forge-std/src/console.sol": { - "keccak256": "0x91d5413c2434ca58fd278b6e1e79fd98d10c83931cc2596a6038eee4daeb34ba", - "urls": [ - "bzz-raw://91ccea707361e48b9b7a161fe81f496b9932bc471e9c4e4e1e9c283f2453cc70", - "dweb:/ipfs/QmcB66sZhQ6Kz7MUHcLE78YXRUZxoZnnxZjN6yATsbB2ec" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol": { - "keccak256": "0x2c309e7df9e05e6ce15bedfe74f3c61b467fc37e0fae9eab496acf5ea0bbd7ff", - "urls": [ - "bzz-raw://7063b5c98711a98018ba4635ac74cee1c1cfa2ea01099498e062699ed9530005", - "dweb:/ipfs/QmeJ8rGXkcv7RrqLdAW8PCXPAykxVsddfYY6g5NaTwmRFE" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol": { - "keccak256": "0x5bce51e11f7d194b79ea59fe00c9e8de9fa2c5530124960f29a24d4c740a3266", - "urls": [ - "bzz-raw://7e66dfde185df46104c11bc89d08fa0760737aa59a2b8546a656473d810a8ea4", - "dweb:/ipfs/QmXvyqtXPaPss2PD7eqPoSao5Szm2n6UMoiG8TZZDjmChR" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol": { - "keccak256": "0xa82b58eca1ee256be466e536706850163d2ec7821945abd6b4778cfb3bee37da", - "urls": [ - "bzz-raw://6e75cf83beb757b8855791088546b8337e9d4684e169400c20d44a515353b708", - "dweb:/ipfs/QmYvPafLfoquiDMEj7CKHtvbgHu7TJNPSVPSCjrtjV8HjV" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Metadata.sol": { - "keccak256": "0x75b829ff2f26c14355d1cba20e16fe7b29ca58eb5fef665ede48bc0f9c6c74b9", - "urls": [ - "bzz-raw://a0a107160525724f9e1bbbab031defc2f298296dd9e331f16a6f7130cec32146", - "dweb:/ipfs/QmemujxSd7gX8A9M8UwmNbz4Ms3U9FG9QfudUgxwvTmPWf" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/utils/Address.sol": { - "keccak256": "0x006dd67219697fe68d7fbfdea512e7c4cb64a43565ed86171d67e844982da6fa", - "urls": [ - "bzz-raw://2455248c8ddd9cc6a7af76a13973cddf222072427e7b0e2a7d1aff345145e931", - "dweb:/ipfs/QmfYjnjRbWqYpuxurqveE6HtzsY1Xx323J428AKQgtBJZm" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/utils/Base64.sol": { - "keccak256": "0x5f3461639fe20794cfb4db4a6d8477388a15b2e70a018043084b7c4bedfa8136", - "urls": [ - "bzz-raw://77e5309e2cc4cdc3395214edb0ff43ff5a5f7373f5a425383e540f6fab530f96", - "dweb:/ipfs/QmTV8DZ9knJDa3b5NPBFQqjvTzodyZVjRUg5mx5A99JPLJ" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/utils/Context.sol": { - "keccak256": "0xe2e337e6dde9ef6b680e07338c493ebea1b5fd09b43424112868e9cc1706bca7", - "urls": [ - "bzz-raw://6df0ddf21ce9f58271bdfaa85cde98b200ef242a05a3f85c2bc10a8294800a92", - "dweb:/ipfs/QmRK2Y5Yc6BK7tGKkgsgn3aJEQGi5aakeSPZvS65PV8Xp3" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/utils/Strings.sol": { - "keccak256": "0x3088eb2868e8d13d89d16670b5f8612c4ab9ff8956272837d8e90106c59c14a0", - "urls": [ - "bzz-raw://b81d9ff6559ea5c47fc573e17ece6d9ba5d6839e213e6ebc3b4c5c8fe4199d7f", - "dweb:/ipfs/QmPCW1bFisUzJkyjroY3yipwfism9RRCigCcK1hbXtVM8n" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol": { - "keccak256": "0xd10975de010d89fd1c78dc5e8a9a7e7f496198085c151648f20cba166b32582b", - "urls": [ - "bzz-raw://fb0048dee081f6fffa5f74afc3fb328483c2a30504e94a0ddd2a5114d731ec4d", - "dweb:/ipfs/QmZptt1nmYoA5SgjwnSgWqgUSDgm4q52Yos3xhnMv3MV43" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol": { - "keccak256": "0x447a5f3ddc18419d41ff92b3773fb86471b1db25773e07f877f548918a185bf1", - "urls": [ - "bzz-raw://be161e54f24e5c6fae81a12db1a8ae87bc5ae1b0ddc805d82a1440a68455088f", - "dweb:/ipfs/QmP7C3CHdY9urF4dEMb9wmsp1wMxHF6nhA2yQE5SKiPAdy" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/utils/math/Math.sol": { - "keccak256": "0xe4455ac1eb7fc497bb7402579e7b4d64d928b846fce7d2b6fde06d366f21c2b3", - "urls": [ - "bzz-raw://cc8841b3cd48ad125e2f46323c8bad3aa0e88e399ec62acb9e57efa7e7c8058c", - "dweb:/ipfs/QmSqE4mXHA2BXW58deDbXE8MTcsL5JSKNDbm23sVQxRLPS" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/utils/math/SignedMath.sol": { - "keccak256": "0xf92515413956f529d95977adc9b0567d583c6203fc31ab1c23824c35187e3ddc", - "urls": [ - "bzz-raw://c50fcc459e49a9858b6d8ad5f911295cb7c9ab57567845a250bf0153f84a95c7", - "dweb:/ipfs/QmcEW85JRzvDkQggxiBBLVAasXWdkhEysqypj9EaB6H2g6" - ], - "license": "MIT" - }, - "src/lib/Structs.sol": { - "keccak256": "0x404e740ae677baf5cc57884ee32d9accb367ae58f56a23f4e59b4f2987ae5100", - "urls": [ - "bzz-raw://349ec5d7e23684f71836001d70c012e59c0f2c94c9fa1fae83f85eeccda1fe52", - "dweb:/ipfs/QmTVT5hzGePT8FxFxWhX77bH1DS1Z8iu8NAfAdn4PfsyCh" - ], - "license": "MIT" - }, - "src/lib/diamond/DiamondCapTable.sol": { - "keccak256": "0x3d21aa24845831867536b58226232fe91be363dc009b01bfd78ceb6d3700f368", - "urls": [ - "bzz-raw://87a8cb5e98e672612777ff542bcf8c6001a110f81f35ff9a90a645cef43da119", - "dweb:/ipfs/Qme7fbxJLQqA76oiDuEJEZeGfNdvBKzGoE9qb6hapWfLhR" - ], - "license": "MIT" - }, - "src/lib/diamond/DiamondCapTableFactory.sol": { - "keccak256": "0xc51a98284ea2222c7048958da187f189a736cbd03c38b3eb63cb9e5e4a3f99c7", - "urls": [ - "bzz-raw://d9c3d613c7e74301be5a983eec00a07da919cf8e9279a327fcd3d0950c65c59c", - "dweb:/ipfs/QmetwDfVFX4krxH2FtBmbS1Aq1phWHwwUYxaGtUyxo9vnR" - ], - "license": "MIT" - }, - "src/lib/diamond/DiamondTxHelper.sol": { - "keccak256": "0xe72ba093a5b16735d0193624d5b6585deb2cfe7e419a468c90fd70317fccf5e6", - "urls": [ - "bzz-raw://4ae3bd1ccbe005c63bed07e255c2b25c9bfdb37b9d85bb75f8f65cfeb0f6df98", - "dweb:/ipfs/QmbTpzVeLkvthzpasQLvuvAfwE3g4sMsjEfpRBNwyG4eMQ" - ], - "license": "MIT" - }, - "src/lib/diamond/Storage.sol": { - "keccak256": "0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f", - "urls": [ - "bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d", - "dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL" - ], - "license": "MIT" - }, - "src/lib/diamond/Structs.sol": { - "keccak256": "0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2", - "urls": [ - "bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab", - "dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/ConvertiblesFacet.sol": { - "keccak256": "0x571d0b98da033ca9b1726bbc2dbe293c906a03fbc295b5a03a90ce173c1a723f", - "urls": [ - "bzz-raw://6402bd044e8c8a738c0f932609c2f0e16eb1eeab58340c6c4b512f773d72e3e7", - "dweb:/ipfs/QmZgJCKhjnsCFM8kvVPGxtzUrk7atjA2CLVi6Cbpf867Ec" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/EquityCompensationFacet.sol": { - "keccak256": "0x12a4fc4b6b5cacb5b1acccc6b8405007e16c3a0f16e639907d6ec16e83780541", - "urls": [ - "bzz-raw://f8dc83374614eefb44872b3bc693dae0d2943e8d3e5840c055ffddea036d4fb6", - "dweb:/ipfs/QmfCah76CqqRcY1FucVNa6MH5wBcnh1DwyCdq4bojb2YFe" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/IssuerFacet.sol": { - "keccak256": "0x7834ab041c438bbc3e641d1b9f30d0bcff7a3e4e120d3251094c6d9ddba4d200", - "urls": [ - "bzz-raw://c15e372f5e4605a7e8ec927937c45fe08f26f18592c63f25dbdc9eb744285005", - "dweb:/ipfs/QmQunUVABRsZkEzyct1rYxuBqavr9fR4NkpqPhZAFvouab" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/StakeholderFacet.sol": { - "keccak256": "0x1d636a399b0bcbe8fa4115b6bd13b09cbc4dfaf73ce862a98419100ddeee56e4", - "urls": [ - "bzz-raw://5748c73c6a7422af0b198ea6c150d21ce5a77413292f55c68543f4a8da8255b9", - "dweb:/ipfs/Qma642YoBMLAdmxPKhks2ki8j6UmCQiH2vyQirJaHXz5eZ" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/StakeholderNFTFacet.sol": { - "keccak256": "0x4f73f0bf6b9c2ed639ae81e84f133283dd08e2c97dae5aa2d73e4d69fbee28c6", - "urls": [ - "bzz-raw://c7226634490391eb3b4919234292d9a62de8fc0d5a8ae53da0541ed594d4347d", - "dweb:/ipfs/QmRoUEwX3V868EyiYqaW9ekHgH9kAH2Vpkgmu8SSpatHMR" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/StockClassFacet.sol": { - "keccak256": "0x3748ae9ad8b1038e75af76911a9e41472428685da93aa0ed754ac7755741305f", - "urls": [ - "bzz-raw://7b930f82dfa1901b5943fc1a2df406b2ccb52da5e7526f2d9a71beb08c562d13", - "dweb:/ipfs/QmRDeck7qdyqeHR5BMBzHfv32jkjG51FHTmgjzgs7Q2NHX" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/StockFacet.sol": { - "keccak256": "0xc6765b22e33ccd04c7c5f65cd2a33a5eb18c9199be9ff941784a3f458445161a", - "urls": [ - "bzz-raw://99eb3c365628ca6a37b9851373d7570ed3cafa9a43f0809754bedbcebcb0b200", - "dweb:/ipfs/QmQE93qCuuWdWZahUCrtEuAWDbKwUKRQC1726yqUPN9WCi" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/StockPlanFacet.sol": { - "keccak256": "0x2f7dae680ad926c2788ce52e65cdb95b31e323c1e04f4e69d304e68e81e4e727", - "urls": [ - "bzz-raw://6d2de2814568867928c4340841625dbd2b0094bc224b1488f03f9799b949fe28", - "dweb:/ipfs/QmU58fHpqy3Bp79ZoshMu6Rewp6PZTzrH8hmCERmy1u35o" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/WarrantFacet.sol": { - "keccak256": "0xf31df59fc2568e1b76c2babcfc2621a425cc83090e7ed0daa773ef4a7bac8773", - "urls": [ - "bzz-raw://b3403064921f8c7ef4cd3b8eb3bf58587bc8c78da0237f58e90081da406080c4", - "dweb:/ipfs/QmYPud1rJR5jzhY19cPor2oPtVm3B3JybbbQBmtxXpjwmw" - ], - "license": "MIT" - }, - "src/lib/diamond/libraries/ValidationLib.sol": { - "keccak256": "0xfcd772b0670acded810468d772729715e3cd4db25c02a0da77c7445903e1cc5f", - "urls": [ - "bzz-raw://d572672df16392825ed86c32a53453247f62b481787db0498993f857623e1ee6", - "dweb:/ipfs/Qmbpoa5EZ7yRcB1mZkmb5nhfxhhFY9g8tMJxPf9ZrzfNfL" - ], - "license": "MIT" - } - }, - "version": 1 - }, - "id": 31 -} \ No newline at end of file diff --git a/chain/out/EquityCompensationFacet.sol/EquityCompensationFacet.json b/chain/out/EquityCompensationFacet.sol/EquityCompensationFacet.json deleted file mode 100644 index c2bcd4d8..00000000 --- a/chain/out/EquityCompensationFacet.sol/EquityCompensationFacet.json +++ /dev/null @@ -1,476 +0,0 @@ -{ - "abi": [ - { - "type": "function", - "name": "exerciseEquityCompensation", - "inputs": [ - { - "name": "equity_comp_security_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "resulting_stock_security_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "quantity", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "getPosition", - "inputs": [ - { - "name": "securityId", - "type": "bytes16", - "internalType": "bytes16" - } - ], - "outputs": [ - { - "name": "", - "type": "tuple", - "internalType": "struct EquityCompensationActivePosition", - "components": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "quantity", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "timestamp", - "type": "uint40", - "internalType": "uint40" - }, - { - "name": "stock_class_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "stock_plan_id", - "type": "bytes16", - "internalType": "bytes16" - } - ] - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "issueEquityCompensation", - "inputs": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "stock_class_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "stock_plan_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "quantity", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "security_id", - "type": "bytes16", - "internalType": "bytes16" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "event", - "name": "TxCreated", - "inputs": [ - { - "name": "txType", - "type": "uint8", - "indexed": false, - "internalType": "enum TxType" - }, - { - "name": "txData", - "type": "bytes", - "indexed": false, - "internalType": "bytes" - } - ], - "anonymous": false - }, - { - "type": "error", - "name": "InsufficientShares", - "inputs": [] - }, - { - "type": "error", - "name": "InvalidQuantity", - "inputs": [] - }, - { - "type": "error", - "name": "InvalidSecurity", - "inputs": [ - { - "name": "security_id", - "type": "bytes16", - "internalType": "bytes16" - } - ] - }, - { - "type": "error", - "name": "InvalidSecurityStakeholder", - "inputs": [ - { - "name": "security_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - } - ] - }, - { - "type": "error", - "name": "InvalidStockClass", - "inputs": [ - { - "name": "stock_class_id", - "type": "bytes16", - "internalType": "bytes16" - } - ] - }, - { - "type": "error", - "name": "NoStakeholder", - "inputs": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - } - ] - } - ], - "bytecode": { - "object": "0x6080806040523461001657610a57908161001c8239f35b600080fdfe6080604052600436101561001257600080fd5b600060e08135811c80630d062fcb146106495780630d08aa0f1461056657636b5e3d221461003f57600080fd5b3461056257606036600319011261056257610058610973565b9161006161098f565b917f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfc61008d81546109a6565b90556001600160801b031984168252600080516020610a378339815191526020526040822090604051916100c0836109cb565b80546001600160801b03199060801b16835260018101546020840152600281015464ffffffffff811660408501526001600160801b03199060581b16606084015260036001600160801b031991015460801b166080830152604435156104bc576020820151156105405760443560208301511061052e576001600160801b0319841683527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfe60205260408320916040519267ffffffffffffffff938060808101108560808301111761051a576080810160405281546001600160801b03198160801b16908183526001600160801b031916602083015260026001840154936040840194855201546060830152156104f8575182516001600160801b031990811691168190036104ce57506044359051036104bc57602081015160443503610469576001600160801b031986168452600080516020610a378339815191526020528360036040822082815582600182015582600282015501557f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c05602052604084206001600160801b031981541690556001600160801b031990511683527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c036020526040832093835b85548082101561045d576102bb82886109fd565b906001600160801b03198a16916001600160801b031991549060031b1c60801b16146102f057506102eb906109a6565b6102a7565b9060009692939495961991828101908111610449579061032761031661034793866109fd565b90549060031b1c60801b91856109fd565b9091906001600160801b0383549160031b9260801c831b921b1916179055565b8154801561043557019061035b82826109fd565b6001600160801b0382549160031b1b19169055555b604051946001600160801b03191660208601526001600160801b03191660408501526044356060850152606084526080840191848310908311176104215781604052600e8252604060a08501528351908160c0860152835b82811061040d57508185010183905291927f88aa35e399ceb8d6b2e810abb3df607c140128e7c089c428f212598bda6a7eb792601f01601f191681010360600190a180f35b8060209187018383820151910152016103c8565b634e487b7160e01b83526041600452602483fd5b634e487b7160e01b87526031600452602487fd5b634e487b7160e01b88526011600452602488fd5b50509091929350610370565b509091926001600160801b031985168452600080516020610a378339815191526020526001604085200180549060443582039182116104a85755610370565b634e487b7160e01b86526011600452602486fd5b60405163524f409b60e01b8152600490fd5b60405163e2adf63b60e01b81526001600160801b0319881660048201526024810191909152604490fd5b60405163236ddac760e11b81526001600160801b031988166004820152602490fd5b634e487b7160e01b86526041600452602486fd5b604051633999656760e01b8152600490fd5b60405163236ddac760e11b81526001600160801b031986166004820152602490fd5b5080fd5b82346106465760203660031901126106465760a0906040610585610973565b918060808351610594816109cb565b828152826020820152828582015282606082015201526001600160801b03198093168152600080516020610a3783398151915260205220906040516105d8816109cb565b8180845460801b1693848352600181015490602084019182526002810154916040850190846080600364ffffffffff9586881686528360608b019860581b1688520154811b970196168652604051978852516020880152511660408601525116606084015251166080820152f35b80fd5b50346105625760a036600319011261056257610663610973565b61066b61098f565b6001600160801b0319916044358381169081900361096f57606435906084358581169384820361096b577f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfc6106c081546109a6565b905586811696878a526020967f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf7885260408b20541561095257811692838b527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf9885260408b2054156109395785156104bc578460038a8a878f8b60408e9281519661074a886109cb565b8752858701928352600080516020610a378339815191528288019664ffffffffff95864216895260608a0197885260808a019b8c5283525220945160801c898654161785555160018501556002840192511674ffffffffffffffffffffffffffffffff00000000008354925160581c16916affffffffffffffffffffff60a81b161717905501905160801c83825416179055888b527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c03885260408b2080549068010000000000000000821015610925578161032791600161082e95940181556109fd565b858a527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c05875260408a209160801c90825416179055604051958587015260408601526060850152608084015260a083015260a0825260c082019282841067ffffffffffffffff85111761091157604090848252600b855283015281519081610100840152845b8281106108fc5782840161012001869052857f88aa35e399ceb8d6b2e810abb3df607c140128e7c089c428f212598bda6a7eb7601f8501601f1916860186900360600187a180f35b808291850161012083820151910152016108b4565b634e487b7160e01b85526041600452602485fd5b634e487b7160e01b8d52604160045260248dfd5b604051630b2b152360e21b815260048101859052602490fd5b60405163b4586dfb60e01b8152600481018a9052602490fd5b8880fd5b8580fd5b600435906001600160801b03198216820361098a57565b600080fd5b602435906001600160801b03198216820361098a57565b60001981146109b55760010190565b634e487b7160e01b600052601160045260246000fd5b60a0810190811067ffffffffffffffff8211176109e757604052565b634e487b7160e01b600052604160045260246000fd5b9190918054831015610a2057600052601060206000208360011c019260041b1690565b634e487b7160e01b600052603260045260246000fdfe2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c04", - "sourceMap": "323:4431:36:-:0;;;;;;;;;;;;;;;;;", - "linkReferences": {} - }, - "deployedBytecode": { - "object": "0x6080604052600436101561001257600080fd5b600060e08135811c80630d062fcb146106495780630d08aa0f1461056657636b5e3d221461003f57600080fd5b3461056257606036600319011261056257610058610973565b9161006161098f565b917f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfc61008d81546109a6565b90556001600160801b031984168252600080516020610a378339815191526020526040822090604051916100c0836109cb565b80546001600160801b03199060801b16835260018101546020840152600281015464ffffffffff811660408501526001600160801b03199060581b16606084015260036001600160801b031991015460801b166080830152604435156104bc576020820151156105405760443560208301511061052e576001600160801b0319841683527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfe60205260408320916040519267ffffffffffffffff938060808101108560808301111761051a576080810160405281546001600160801b03198160801b16908183526001600160801b031916602083015260026001840154936040840194855201546060830152156104f8575182516001600160801b031990811691168190036104ce57506044359051036104bc57602081015160443503610469576001600160801b031986168452600080516020610a378339815191526020528360036040822082815582600182015582600282015501557f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c05602052604084206001600160801b031981541690556001600160801b031990511683527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c036020526040832093835b85548082101561045d576102bb82886109fd565b906001600160801b03198a16916001600160801b031991549060031b1c60801b16146102f057506102eb906109a6565b6102a7565b9060009692939495961991828101908111610449579061032761031661034793866109fd565b90549060031b1c60801b91856109fd565b9091906001600160801b0383549160031b9260801c831b921b1916179055565b8154801561043557019061035b82826109fd565b6001600160801b0382549160031b1b19169055555b604051946001600160801b03191660208601526001600160801b03191660408501526044356060850152606084526080840191848310908311176104215781604052600e8252604060a08501528351908160c0860152835b82811061040d57508185010183905291927f88aa35e399ceb8d6b2e810abb3df607c140128e7c089c428f212598bda6a7eb792601f01601f191681010360600190a180f35b8060209187018383820151910152016103c8565b634e487b7160e01b83526041600452602483fd5b634e487b7160e01b87526031600452602487fd5b634e487b7160e01b88526011600452602488fd5b50509091929350610370565b509091926001600160801b031985168452600080516020610a378339815191526020526001604085200180549060443582039182116104a85755610370565b634e487b7160e01b86526011600452602486fd5b60405163524f409b60e01b8152600490fd5b60405163e2adf63b60e01b81526001600160801b0319881660048201526024810191909152604490fd5b60405163236ddac760e11b81526001600160801b031988166004820152602490fd5b634e487b7160e01b86526041600452602486fd5b604051633999656760e01b8152600490fd5b60405163236ddac760e11b81526001600160801b031986166004820152602490fd5b5080fd5b82346106465760203660031901126106465760a0906040610585610973565b918060808351610594816109cb565b828152826020820152828582015282606082015201526001600160801b03198093168152600080516020610a3783398151915260205220906040516105d8816109cb565b8180845460801b1693848352600181015490602084019182526002810154916040850190846080600364ffffffffff9586881686528360608b019860581b1688520154811b970196168652604051978852516020880152511660408601525116606084015251166080820152f35b80fd5b50346105625760a036600319011261056257610663610973565b61066b61098f565b6001600160801b0319916044358381169081900361096f57606435906084358581169384820361096b577f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfc6106c081546109a6565b905586811696878a526020967f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf7885260408b20541561095257811692838b527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf9885260408b2054156109395785156104bc578460038a8a878f8b60408e9281519661074a886109cb565b8752858701928352600080516020610a378339815191528288019664ffffffffff95864216895260608a0197885260808a019b8c5283525220945160801c898654161785555160018501556002840192511674ffffffffffffffffffffffffffffffff00000000008354925160581c16916affffffffffffffffffffff60a81b161717905501905160801c83825416179055888b527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c03885260408b2080549068010000000000000000821015610925578161032791600161082e95940181556109fd565b858a527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c05875260408a209160801c90825416179055604051958587015260408601526060850152608084015260a083015260a0825260c082019282841067ffffffffffffffff85111761091157604090848252600b855283015281519081610100840152845b8281106108fc5782840161012001869052857f88aa35e399ceb8d6b2e810abb3df607c140128e7c089c428f212598bda6a7eb7601f8501601f1916860186900360600187a180f35b808291850161012083820151910152016108b4565b634e487b7160e01b85526041600452602485fd5b634e487b7160e01b8d52604160045260248dfd5b604051630b2b152360e21b815260048101859052602490fd5b60405163b4586dfb60e01b8152600481018a9052602490fd5b8880fd5b8580fd5b600435906001600160801b03198216820361098a57565b600080fd5b602435906001600160801b03198216820361098a57565b60001981146109b55760010190565b634e487b7160e01b600052601160045260246000fd5b60a0810190811067ffffffffffffffff8211176109e757604052565b634e487b7160e01b600052604160045260246000fd5b9190918054831015610a2057600052601060206000208360011c019260041b1690565b634e487b7160e01b600052603260045260246000fdfe2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c04", - "sourceMap": "323:4431:36:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;323:4431:36;;;;;;:::i;:::-;;;;:::i;:::-;1863:8;;:10;323:4431;;1863:10;:::i;:::-;323:4431;;-1:-1:-1;;;;;323:4431:36;;;;;-1:-1:-1;;;;;;;;;;;323:4431:36;;;;;;;;;;;;:::i;:::-;;;-1:-1:-1;;;;;323:4431:36;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;323:4431:36;;;;;;;;;;-1:-1:-1;;;;;323:4431:36;;;;;;;;;;;;;2112:13;2108:82;;323:4431;;;;2203:28;2199:120;;323:4431;;;;;;2332:34;2328:106;;-1:-1:-1;;;;;323:4431:36;;;;;2561:34;323:4431;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;323:4431:36;;;;;;;;;-1:-1:-1;;;;;323:4431:36;;;;;;;;;;;;;;;;;;;;;;;;2638:42;2634:138;;323:4431;;;-1:-1:-1;;;;;;323:4431:36;;;;;2785:61;;;2781:199;;323:4431;;;;;3067:34;3063:103;;323:4431;;;;;;3231:35;323:4431;;-1:-1:-1;;;;;323:4431:36;;;;;-1:-1:-1;;;;;;;;;;;323:4431:36;;;;;;;;;;;;;;;;;;;;;;3446:58;323:4431;;;;;-1:-1:-1;;;;;323:4431:36;;;;;;-1:-1:-1;;;;;323:4431:36;;;;;;2025:36;323:4431;;;;;3767:10;;3813:3;323:4431;;3779:32;;;;;;3840:24;;;;:::i;:::-;323:4431;-1:-1:-1;;;;;323:4431:36;;;;-1:-1:-1;;;;;323:4431:36;;;;;;;;;;3840:51;3836:256;;3813:3;;;;:::i;:::-;3767:10;;3836:256;323:4431;;;;;;;;;;;;;;;;;;3942:55;3915:24;3942:55;3915:82;3942:55;;;:::i;:::-;323:4431;;;;;;;;3915:24;;;:::i;:::-;:82;323:4431;;-1:-1:-1;;;;;323:4431:36;;;;;;;;;;;;;;;;;;3915:82;323:4431;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;323:4431:36;;;;;;;;;;;3762:344;323:4431;;;-1:-1:-1;;;;;323:4431:36;;;4358:74;;323:4431;-1:-1:-1;;;;;323:4431:36;;;;;;;;;;;;;4358:74;;323:4431;;;;;;;;;;;;;;;;4460:35;323:4431;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;323:4431:36;;;;;;;;;1098:25:32;;323:4431:36;;-1:-1:-1;;323:4431:36;;;1098:25:32;323:4431:36;1098:25:32;;;323:4431:36;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;323:4431:36;;;;;;;;;-1:-1:-1;;;323:4431:36;;;;;;;;;-1:-1:-1;;;323:4431:36;;;;;;;;3779:32;;;;;;;;;;3227:1071;323:4431;;;;-1:-1:-1;;;;;323:4431:36;;;;;-1:-1:-1;;;;;;;;;;;323:4431:36;;;;;;4194:81;323:4431;;;;;;;;;;;;;3227:1071;;323:4431;-1:-1:-1;;;323:4431:36;;;;;;;;3063:103;323:4431;;-1:-1:-1;;;3124:31:36;;323:4431;;3124:31;2781:199;323:4431;;-1:-1:-1;;;2869:100:36;;-1:-1:-1;;;;;;323:4431:36;;;2869:100;;323:4431;;;;;;;;;;2869:100;2634:138;323:4431;;-1:-1:-1;;;2703:58:36;;-1:-1:-1;;;;;;323:4431:36;;;2703:58;;323:4431;;;2703:58;323:4431;-1:-1:-1;;;323:4431:36;;;;;;;;2328:106;323:4431;;-1:-1:-1;;;2389:34:36;;323:4431;;2389:34;2199:120;323:4431;;-1:-1:-1;;;2254:54:36;;-1:-1:-1;;;;;;323:4431:36;;;2254:54;;323:4431;;;2254:54;323:4431;;;;;;;;;;;-1:-1:-1;;323:4431:36;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;323:4431:36;;;;;;-1:-1:-1;;;;;;;;;;;323:4431:36;;;;;;;;;:::i;:::-;;;;;;;;;;;;4686:47;323:4431;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;323:4431:36;;;;;;:::i;:::-;;;:::i;:::-;-1:-1:-1;;;;;;323:4431:36;;;;;;;;;;;;;;;;;;;;;;;;;;617:8;:10;323:4431;;617:10;:::i;:::-;323:4431;;;;;;;;;;;686:19:44;323:4431:36;;;;;;686:40:44;682:107;;323:4431:36;;;;;;928:18:44;323:4431:36;;;;;;928:39:44;924:110;;1357:13;;1353:43;;323:4431:36;;;;;;;;;;;;;;;;:::i;:::-;;;906:254;;;323:4431;;;-1:-1:-1;;;;;;;;;;;906:254:36;;;323:4431;;1047:15;;;323:4431;;;;906:254;;323:4431;;;;906:254;;323:4431;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;843:36;323:4431;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;1358:58;323:4431;;;;;;;;;;;;;;;;;1508:80;;;;323:4431;;;;;;;;;;;;;;;;;;1508:80;;323:4431;;;;;;;;;;;;;;;;;;1616:35;323:4431;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1098:25:32;323:4431:36;;;-1:-1:-1;;323:4431:36;;;1098:25:32;;;323:4431:36;1098:25:32;323:4431:36;1098:25:32;323:4431:36;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;323:4431:36;;;;;;;;;-1:-1:-1;;;323:4431:36;;;;;;;;924:110:44;323:4431:36;;-1:-1:-1;;;990:33:44;;323:4431:36;990:33:44;;323:4431:36;;;;;990:33:44;682:107;323:4431:36;;-1:-1:-1;;;749:29:44;;323:4431:36;749:29:44;;323:4431:36;;;;;749:29:44;323:4431:36;;;;;;;;;;;;-1:-1:-1;;;;;;323:4431:36;;;;;;:::o;:::-;;;;;;;;-1:-1:-1;;;;;;323:4431:36;;;;;;:::o;:::-;-1:-1:-1;;323:4431:36;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;323:4431:36;;;-1:-1:-1;323:4431:36;;;;;;;;;;:::o;:::-;;;;;;;;;;;", - "linkReferences": {} - }, - "methodIdentifiers": { - "exerciseEquityCompensation(bytes16,bytes16,uint256)": "6b5e3d22", - "getPosition(bytes16)": "0d08aa0f", - "issueEquityCompensation(bytes16,bytes16,bytes16,uint256,bytes16)": "0d062fcb" - }, - "rawMetadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"InsufficientShares\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidQuantity\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"security_id\",\"type\":\"bytes16\"}],\"name\":\"InvalidSecurity\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"security_id\",\"type\":\"bytes16\"},{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"}],\"name\":\"InvalidSecurityStakeholder\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stock_class_id\",\"type\":\"bytes16\"}],\"name\":\"InvalidStockClass\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"}],\"name\":\"NoStakeholder\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"enum TxType\",\"name\":\"txType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"txData\",\"type\":\"bytes\"}],\"name\":\"TxCreated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"equity_comp_security_id\",\"type\":\"bytes16\"},{\"internalType\":\"bytes16\",\"name\":\"resulting_stock_security_id\",\"type\":\"bytes16\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"name\":\"exerciseEquityCompensation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"securityId\",\"type\":\"bytes16\"}],\"name\":\"getPosition\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"},{\"internalType\":\"uint40\",\"name\":\"timestamp\",\"type\":\"uint40\"},{\"internalType\":\"bytes16\",\"name\":\"stock_class_id\",\"type\":\"bytes16\"},{\"internalType\":\"bytes16\",\"name\":\"stock_plan_id\",\"type\":\"bytes16\"}],\"internalType\":\"struct EquityCompensationActivePosition\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"},{\"internalType\":\"bytes16\",\"name\":\"stock_class_id\",\"type\":\"bytes16\"},{\"internalType\":\"bytes16\",\"name\":\"stock_plan_id\",\"type\":\"bytes16\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"},{\"internalType\":\"bytes16\",\"name\":\"security_id\",\"type\":\"bytes16\"}],\"name\":\"issueEquityCompensation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/lib/diamond/facets/EquityCompensationFacet.sol\":\"EquityCompensationFacet\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"appendCBOR\":false,\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@diamond/=src/lib/diamond/\",\":diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/\",\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\",\":openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/\"],\"viaIR\":true},\"sources\":{\"src/lib/Structs.sol\":{\"keccak256\":\"0x404e740ae677baf5cc57884ee32d9accb367ae58f56a23f4e59b4f2987ae5100\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://349ec5d7e23684f71836001d70c012e59c0f2c94c9fa1fae83f85eeccda1fe52\",\"dweb:/ipfs/QmTVT5hzGePT8FxFxWhX77bH1DS1Z8iu8NAfAdn4PfsyCh\"]},\"src/lib/diamond/DiamondTxHelper.sol\":{\"keccak256\":\"0xe72ba093a5b16735d0193624d5b6585deb2cfe7e419a468c90fd70317fccf5e6\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://4ae3bd1ccbe005c63bed07e255c2b25c9bfdb37b9d85bb75f8f65cfeb0f6df98\",\"dweb:/ipfs/QmbTpzVeLkvthzpasQLvuvAfwE3g4sMsjEfpRBNwyG4eMQ\"]},\"src/lib/diamond/Storage.sol\":{\"keccak256\":\"0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d\",\"dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL\"]},\"src/lib/diamond/Structs.sol\":{\"keccak256\":\"0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab\",\"dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD\"]},\"src/lib/diamond/facets/EquityCompensationFacet.sol\":{\"keccak256\":\"0x12a4fc4b6b5cacb5b1acccc6b8405007e16c3a0f16e639907d6ec16e83780541\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://f8dc83374614eefb44872b3bc693dae0d2943e8d3e5840c055ffddea036d4fb6\",\"dweb:/ipfs/QmfCah76CqqRcY1FucVNa6MH5wBcnh1DwyCdq4bojb2YFe\"]},\"src/lib/diamond/libraries/ValidationLib.sol\":{\"keccak256\":\"0xfcd772b0670acded810468d772729715e3cd4db25c02a0da77c7445903e1cc5f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d572672df16392825ed86c32a53453247f62b481787db0498993f857623e1ee6\",\"dweb:/ipfs/Qmbpoa5EZ7yRcB1mZkmb5nhfxhhFY9g8tMJxPf9ZrzfNfL\"]}},\"version\":1}", - "metadata": { - "compiler": { - "version": "0.8.20+commit.a1b79de6" - }, - "language": "Solidity", - "output": { - "abi": [ - { - "inputs": [], - "type": "error", - "name": "InsufficientShares" - }, - { - "inputs": [], - "type": "error", - "name": "InvalidQuantity" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "security_id", - "type": "bytes16" - } - ], - "type": "error", - "name": "InvalidSecurity" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "security_id", - "type": "bytes16" - }, - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - } - ], - "type": "error", - "name": "InvalidSecurityStakeholder" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stock_class_id", - "type": "bytes16" - } - ], - "type": "error", - "name": "InvalidStockClass" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - } - ], - "type": "error", - "name": "NoStakeholder" - }, - { - "inputs": [ - { - "internalType": "enum TxType", - "name": "txType", - "type": "uint8", - "indexed": false - }, - { - "internalType": "bytes", - "name": "txData", - "type": "bytes", - "indexed": false - } - ], - "type": "event", - "name": "TxCreated", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "equity_comp_security_id", - "type": "bytes16" - }, - { - "internalType": "bytes16", - "name": "resulting_stock_security_id", - "type": "bytes16" - }, - { - "internalType": "uint256", - "name": "quantity", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "exerciseEquityCompensation" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "securityId", - "type": "bytes16" - } - ], - "stateMutability": "view", - "type": "function", - "name": "getPosition", - "outputs": [ - { - "internalType": "struct EquityCompensationActivePosition", - "name": "", - "type": "tuple", - "components": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - }, - { - "internalType": "uint256", - "name": "quantity", - "type": "uint256" - }, - { - "internalType": "uint40", - "name": "timestamp", - "type": "uint40" - }, - { - "internalType": "bytes16", - "name": "stock_class_id", - "type": "bytes16" - }, - { - "internalType": "bytes16", - "name": "stock_plan_id", - "type": "bytes16" - } - ] - } - ] - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - }, - { - "internalType": "bytes16", - "name": "stock_class_id", - "type": "bytes16" - }, - { - "internalType": "bytes16", - "name": "stock_plan_id", - "type": "bytes16" - }, - { - "internalType": "uint256", - "name": "quantity", - "type": "uint256" - }, - { - "internalType": "bytes16", - "name": "security_id", - "type": "bytes16" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "issueEquityCompensation" - } - ], - "devdoc": { - "kind": "dev", - "methods": {}, - "version": 1 - }, - "userdoc": { - "kind": "user", - "methods": {}, - "version": 1 - } - }, - "settings": { - "remappings": [ - "@diamond/=src/lib/diamond/", - "diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/", - "ds-test/=lib/forge-std/lib/ds-test/src/", - "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", - "forge-std/=lib/forge-std/src/", - "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", - "openzeppelin-contracts/=lib/openzeppelin-contracts/", - "openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/" - ], - "optimizer": { - "enabled": true, - "runs": 200 - }, - "metadata": { - "bytecodeHash": "none", - "appendCBOR": false - }, - "compilationTarget": { - "src/lib/diamond/facets/EquityCompensationFacet.sol": "EquityCompensationFacet" - }, - "evmVersion": "paris", - "libraries": {}, - "viaIR": true - }, - "sources": { - "src/lib/Structs.sol": { - "keccak256": "0x404e740ae677baf5cc57884ee32d9accb367ae58f56a23f4e59b4f2987ae5100", - "urls": [ - "bzz-raw://349ec5d7e23684f71836001d70c012e59c0f2c94c9fa1fae83f85eeccda1fe52", - "dweb:/ipfs/QmTVT5hzGePT8FxFxWhX77bH1DS1Z8iu8NAfAdn4PfsyCh" - ], - "license": "MIT" - }, - "src/lib/diamond/DiamondTxHelper.sol": { - "keccak256": "0xe72ba093a5b16735d0193624d5b6585deb2cfe7e419a468c90fd70317fccf5e6", - "urls": [ - "bzz-raw://4ae3bd1ccbe005c63bed07e255c2b25c9bfdb37b9d85bb75f8f65cfeb0f6df98", - "dweb:/ipfs/QmbTpzVeLkvthzpasQLvuvAfwE3g4sMsjEfpRBNwyG4eMQ" - ], - "license": "MIT" - }, - "src/lib/diamond/Storage.sol": { - "keccak256": "0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f", - "urls": [ - "bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d", - "dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL" - ], - "license": "MIT" - }, - "src/lib/diamond/Structs.sol": { - "keccak256": "0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2", - "urls": [ - "bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab", - "dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/EquityCompensationFacet.sol": { - "keccak256": "0x12a4fc4b6b5cacb5b1acccc6b8405007e16c3a0f16e639907d6ec16e83780541", - "urls": [ - "bzz-raw://f8dc83374614eefb44872b3bc693dae0d2943e8d3e5840c055ffddea036d4fb6", - "dweb:/ipfs/QmfCah76CqqRcY1FucVNa6MH5wBcnh1DwyCdq4bojb2YFe" - ], - "license": "MIT" - }, - "src/lib/diamond/libraries/ValidationLib.sol": { - "keccak256": "0xfcd772b0670acded810468d772729715e3cd4db25c02a0da77c7445903e1cc5f", - "urls": [ - "bzz-raw://d572672df16392825ed86c32a53453247f62b481787db0498993f857623e1ee6", - "dweb:/ipfs/Qmbpoa5EZ7yRcB1mZkmb5nhfxhhFY9g8tMJxPf9ZrzfNfL" - ], - "license": "MIT" - } - }, - "version": 1 - }, - "id": 36 -} \ No newline at end of file diff --git a/chain/out/IssuerFacet.sol/IssuerFacet.json b/chain/out/IssuerFacet.sol/IssuerFacet.json deleted file mode 100644 index acad448c..00000000 --- a/chain/out/IssuerFacet.sol/IssuerFacet.json +++ /dev/null @@ -1,269 +0,0 @@ -{ - "abi": [ - { - "type": "function", - "name": "adjustIssuerAuthorizedShares", - "inputs": [ - { - "name": "newSharesAuthorized", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "initializeIssuer", - "inputs": [ - { - "name": "id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "initial_shares_authorized", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "event", - "name": "IssuerAuthorizedSharesAdjusted", - "inputs": [ - { - "name": "newSharesAuthorized", - "type": "uint256", - "indexed": false, - "internalType": "uint256" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "TxCreated", - "inputs": [ - { - "name": "txType", - "type": "uint8", - "indexed": false, - "internalType": "enum TxType" - }, - { - "name": "txData", - "type": "bytes", - "indexed": false, - "internalType": "bytes" - } - ], - "anonymous": false - }, - { - "type": "error", - "name": "InvalidSharesAuthorized", - "inputs": [] - }, - { - "type": "error", - "name": "IssuerAlreadyInitialized", - "inputs": [] - } - ], - "bytecode": { - "object": "0x608080604052346100165761023a908161001c8239f35b600080fdfe608060408181526004908136101561001657600080fd5b600092833560e01c9081632f539c45146101495750633f7a08e31461003a57600080fd5b346101455760208060031936011261014157823591827f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf5557fc4f6dc90809178743bbf231e348ffbad613598a05f67b2cc824c4a12ceffdde1828251858152a1805192828401528183528083019383851067ffffffffffffffff86111761012e5750838152600184526060830152815190816080840152845b82811061011a5782840160a001869052857f88aa35e399ceb8d6b2e810abb3df607c140128e7c089c428f212598bda6a7eb7601f8501601f1916860186900360600187a180f35b808291850160a083820151910152016100d3565b634e487b7160e01b865260419052602485fd5b8380fd5b8280fd5b92919050346101415780600319360112610141578135916fffffffffffffffffffffffffffffffff19918284168403610236577f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf594855461022857505167ffffffffffffffff6060820191821091111761021557507f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf39160801c90825416179055817f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf455602435905580f35b634e487b7160e01b855260419052602484fd5b63334bd48b60e21b81529050fd5b8580fd", - "sourceMap": "285:891:37:-:0;;;;;;;;;;;;;;;;;", - "linkReferences": {} - }, - "deployedBytecode": { - "object": "0x608060408181526004908136101561001657600080fd5b600092833560e01c9081632f539c45146101495750633f7a08e31461003a57600080fd5b346101455760208060031936011261014157823591827f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf5557fc4f6dc90809178743bbf231e348ffbad613598a05f67b2cc824c4a12ceffdde1828251858152a1805192828401528183528083019383851067ffffffffffffffff86111761012e5750838152600184526060830152815190816080840152845b82811061011a5782840160a001869052857f88aa35e399ceb8d6b2e810abb3df607c140128e7c089c428f212598bda6a7eb7601f8501601f1916860186900360600187a180f35b808291850160a083820151910152016100d3565b634e487b7160e01b865260419052602485fd5b8380fd5b8280fd5b92919050346101415780600319360112610141578135916fffffffffffffffffffffffffffffffff19918284168403610236577f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf594855461022857505167ffffffffffffffff6060820191821091111761021557507f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf39160801c90825416179055817f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf455602435905580f35b634e487b7160e01b855260419052602484fd5b63334bd48b60e21b81529050fd5b8580fd", - "sourceMap": "285:891:37:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;947:27;285:891;1012:51;285:891;;;;;;1012:51;285:891;;1135:31;;;;285:891;1135:31;;;285:891;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1098:25:32;285:891:37;;;-1:-1:-1;;285:891:37;;;1098:25:32;;;285:891:37;1098:25:32;285:891:37;1098:25:32;285:891:37;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;285:891:37;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;285:891:37;;;;;;;;604:27;285:891;;;600:96;;285:891;;;;;;;;;;;;;;876:28:33;;285:891:37;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;285:891:37;;;;;;;;600:96;-1:-1:-1;;;659:26:37;;;-1:-1:-1;659:26:37;285:891;;;", - "linkReferences": {} - }, - "methodIdentifiers": { - "adjustIssuerAuthorizedShares(uint256)": "3f7a08e3", - "initializeIssuer(bytes16,uint256)": "2f539c45" - }, - "rawMetadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"InvalidSharesAuthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IssuerAlreadyInitialized\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newSharesAuthorized\",\"type\":\"uint256\"}],\"name\":\"IssuerAuthorizedSharesAdjusted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"enum TxType\",\"name\":\"txType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"txData\",\"type\":\"bytes\"}],\"name\":\"TxCreated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newSharesAuthorized\",\"type\":\"uint256\"}],\"name\":\"adjustIssuerAuthorizedShares\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"id\",\"type\":\"bytes16\"},{\"internalType\":\"uint256\",\"name\":\"initial_shares_authorized\",\"type\":\"uint256\"}],\"name\":\"initializeIssuer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/lib/diamond/facets/IssuerFacet.sol\":\"IssuerFacet\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"appendCBOR\":false,\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@diamond/=src/lib/diamond/\",\":diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/\",\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\",\":openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/\"],\"viaIR\":true},\"sources\":{\"lib/diamond-3-hardhat/contracts/interfaces/IDiamondCut.sol\":{\"keccak256\":\"0xc00c16bfa30a3fa5f3dc684f7f8ba62c259962b25f647d9588739458989717fc\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://119d9a5acd99b9462a8341c9b95ddd468648799eefa47038f81521431743c1ae\",\"dweb:/ipfs/QmTF7WNyPWTUtUzNcpq5rf5v2uw5TwzqsSg9D53pfQufcu\"]},\"lib/diamond-3-hardhat/contracts/libraries/LibDiamond.sol\":{\"keccak256\":\"0xc5044f5a7a031e4e1869a26addf83b25c8b20d5949ba13b613dfbc72ad2f63b0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://25f2ac88867d97bfd13b503c06512b082ecf861f3f00702ca2747502c9113a79\",\"dweb:/ipfs/QmSYDNeaLGXDsPa2maoaeim5qJiLNuM3PbDguoYmsUmAZL\"]},\"src/lib/Structs.sol\":{\"keccak256\":\"0x404e740ae677baf5cc57884ee32d9accb367ae58f56a23f4e59b4f2987ae5100\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://349ec5d7e23684f71836001d70c012e59c0f2c94c9fa1fae83f85eeccda1fe52\",\"dweb:/ipfs/QmTVT5hzGePT8FxFxWhX77bH1DS1Z8iu8NAfAdn4PfsyCh\"]},\"src/lib/diamond/DiamondTxHelper.sol\":{\"keccak256\":\"0xe72ba093a5b16735d0193624d5b6585deb2cfe7e419a468c90fd70317fccf5e6\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://4ae3bd1ccbe005c63bed07e255c2b25c9bfdb37b9d85bb75f8f65cfeb0f6df98\",\"dweb:/ipfs/QmbTpzVeLkvthzpasQLvuvAfwE3g4sMsjEfpRBNwyG4eMQ\"]},\"src/lib/diamond/Storage.sol\":{\"keccak256\":\"0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d\",\"dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL\"]},\"src/lib/diamond/Structs.sol\":{\"keccak256\":\"0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab\",\"dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD\"]},\"src/lib/diamond/facets/IssuerFacet.sol\":{\"keccak256\":\"0x7834ab041c438bbc3e641d1b9f30d0bcff7a3e4e120d3251094c6d9ddba4d200\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://c15e372f5e4605a7e8ec927937c45fe08f26f18592c63f25dbdc9eb744285005\",\"dweb:/ipfs/QmQunUVABRsZkEzyct1rYxuBqavr9fR4NkpqPhZAFvouab\"]}},\"version\":1}", - "metadata": { - "compiler": { - "version": "0.8.20+commit.a1b79de6" - }, - "language": "Solidity", - "output": { - "abi": [ - { - "inputs": [], - "type": "error", - "name": "InvalidSharesAuthorized" - }, - { - "inputs": [], - "type": "error", - "name": "IssuerAlreadyInitialized" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "newSharesAuthorized", - "type": "uint256", - "indexed": false - } - ], - "type": "event", - "name": "IssuerAuthorizedSharesAdjusted", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "enum TxType", - "name": "txType", - "type": "uint8", - "indexed": false - }, - { - "internalType": "bytes", - "name": "txData", - "type": "bytes", - "indexed": false - } - ], - "type": "event", - "name": "TxCreated", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "newSharesAuthorized", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "adjustIssuerAuthorizedShares" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "id", - "type": "bytes16" - }, - { - "internalType": "uint256", - "name": "initial_shares_authorized", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "initializeIssuer" - } - ], - "devdoc": { - "kind": "dev", - "methods": {}, - "version": 1 - }, - "userdoc": { - "kind": "user", - "methods": {}, - "version": 1 - } - }, - "settings": { - "remappings": [ - "@diamond/=src/lib/diamond/", - "diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/", - "ds-test/=lib/forge-std/lib/ds-test/src/", - "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", - "forge-std/=lib/forge-std/src/", - "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", - "openzeppelin-contracts/=lib/openzeppelin-contracts/", - "openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/" - ], - "optimizer": { - "enabled": true, - "runs": 200 - }, - "metadata": { - "bytecodeHash": "none", - "appendCBOR": false - }, - "compilationTarget": { - "src/lib/diamond/facets/IssuerFacet.sol": "IssuerFacet" - }, - "evmVersion": "paris", - "libraries": {}, - "viaIR": true - }, - "sources": { - "lib/diamond-3-hardhat/contracts/interfaces/IDiamondCut.sol": { - "keccak256": "0xc00c16bfa30a3fa5f3dc684f7f8ba62c259962b25f647d9588739458989717fc", - "urls": [ - "bzz-raw://119d9a5acd99b9462a8341c9b95ddd468648799eefa47038f81521431743c1ae", - "dweb:/ipfs/QmTF7WNyPWTUtUzNcpq5rf5v2uw5TwzqsSg9D53pfQufcu" - ], - "license": "MIT" - }, - "lib/diamond-3-hardhat/contracts/libraries/LibDiamond.sol": { - "keccak256": "0xc5044f5a7a031e4e1869a26addf83b25c8b20d5949ba13b613dfbc72ad2f63b0", - "urls": [ - "bzz-raw://25f2ac88867d97bfd13b503c06512b082ecf861f3f00702ca2747502c9113a79", - "dweb:/ipfs/QmSYDNeaLGXDsPa2maoaeim5qJiLNuM3PbDguoYmsUmAZL" - ], - "license": "MIT" - }, - "src/lib/Structs.sol": { - "keccak256": "0x404e740ae677baf5cc57884ee32d9accb367ae58f56a23f4e59b4f2987ae5100", - "urls": [ - "bzz-raw://349ec5d7e23684f71836001d70c012e59c0f2c94c9fa1fae83f85eeccda1fe52", - "dweb:/ipfs/QmTVT5hzGePT8FxFxWhX77bH1DS1Z8iu8NAfAdn4PfsyCh" - ], - "license": "MIT" - }, - "src/lib/diamond/DiamondTxHelper.sol": { - "keccak256": "0xe72ba093a5b16735d0193624d5b6585deb2cfe7e419a468c90fd70317fccf5e6", - "urls": [ - "bzz-raw://4ae3bd1ccbe005c63bed07e255c2b25c9bfdb37b9d85bb75f8f65cfeb0f6df98", - "dweb:/ipfs/QmbTpzVeLkvthzpasQLvuvAfwE3g4sMsjEfpRBNwyG4eMQ" - ], - "license": "MIT" - }, - "src/lib/diamond/Storage.sol": { - "keccak256": "0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f", - "urls": [ - "bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d", - "dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL" - ], - "license": "MIT" - }, - "src/lib/diamond/Structs.sol": { - "keccak256": "0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2", - "urls": [ - "bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab", - "dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/IssuerFacet.sol": { - "keccak256": "0x7834ab041c438bbc3e641d1b9f30d0bcff7a3e4e120d3251094c6d9ddba4d200", - "urls": [ - "bzz-raw://c15e372f5e4605a7e8ec927937c45fe08f26f18592c63f25dbdc9eb744285005", - "dweb:/ipfs/QmQunUVABRsZkEzyct1rYxuBqavr9fR4NkpqPhZAFvouab" - ], - "license": "MIT" - } - }, - "version": 1 - }, - "id": 37 -} \ No newline at end of file diff --git a/chain/out/StakeholderFacet.sol/StakeholderFacet.json b/chain/out/StakeholderFacet.sol/StakeholderFacet.json deleted file mode 100644 index 01a83243..00000000 --- a/chain/out/StakeholderFacet.sol/StakeholderFacet.json +++ /dev/null @@ -1,490 +0,0 @@ -{ - "abi": [ - { - "type": "function", - "name": "createStakeholder", - "inputs": [ - { - "name": "_id", - "type": "bytes16", - "internalType": "bytes16" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "getStakeholderPositions", - "inputs": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - } - ], - "outputs": [ - { - "name": "", - "type": "tuple", - "internalType": "struct StakeholderPositions", - "components": [ - { - "name": "stocks", - "type": "tuple[]", - "internalType": "struct StockActivePosition[]", - "components": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "stock_class_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "quantity", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "share_price", - "type": "uint256", - "internalType": "uint256" - } - ] - }, - { - "name": "warrants", - "type": "tuple[]", - "internalType": "struct WarrantActivePosition[]", - "components": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "quantity", - "type": "uint256", - "internalType": "uint256" - } - ] - }, - { - "name": "convertibles", - "type": "tuple[]", - "internalType": "struct ConvertibleActivePosition[]", - "components": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "investment_amount", - "type": "uint256", - "internalType": "uint256" - } - ] - }, - { - "name": "equityCompensations", - "type": "tuple[]", - "internalType": "struct EquityCompensationActivePosition[]", - "components": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "quantity", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "timestamp", - "type": "uint40", - "internalType": "uint40" - }, - { - "name": "stock_class_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "stock_plan_id", - "type": "bytes16", - "internalType": "bytes16" - } - ] - } - ] - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "linkStakeholderAddress", - "inputs": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "wallet_address", - "type": "address", - "internalType": "address" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "event", - "name": "StakeholderAddressLinked", - "inputs": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "indexed": true, - "internalType": "bytes16" - }, - { - "name": "wallet_address", - "type": "address", - "indexed": true, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "StakeholderCreated", - "inputs": [ - { - "name": "id", - "type": "bytes16", - "indexed": true, - "internalType": "bytes16" - } - ], - "anonymous": false - }, - { - "type": "error", - "name": "AddressAlreadyLinked", - "inputs": [ - { - "name": "wallet_address", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "StakeholderAlreadyExists", - "inputs": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - } - ] - } - ], - "bytecode": { - "object": "0x6080806040523461001657610a8a908161001c8239f35b600080fdfe604060808152600436101561001357600080fd5b600090813560e01c80636d6cf824146102965780639e35325f146101445763eafa8a5f1461004057600080fd5b346101405780600319360112610140576100586108ff565b6024356001600160a01b03811692919083810361013c576001600160a01b03811660009081527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c0960205260409020546001600160801b0319939060801b841661012557506001600160a01b031660009081527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c09602052604090208160801c83825416179055167fcb95900f20209676b1525cc13b65d7da43bed8372441da3bef171a88d91c54d68380a380f35b5163618ab76160e11b815260048101859052602490fd5b8480fd5b5080fd5b50346101405760203660031901126101405761015e6108ff565b6001600160801b03198116918284527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf791826020528185205461027e577f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf6908154906801000000000000000082101561026a5760018201808455821015610256578160011c7ff914494c1c9f5225b90a8394780d36857ab4e0eda6db8a41ed9ef9647838916501916001600160801b03608084549260071b169260801c831b921b191617905554918385526020528320557f53df47344d1cdf2ddb4901af5df61e37e14606bb7c8cc004d65c7c83ab3d06938280a280f35b634e487b7160e01b87526032600452602487fd5b634e487b7160e01b87526041600452602487fd5b81516333270c6160e01b815260048101859052602490fd5b5090346108fc57602090816003193601126108fc5791906102b56108ff565b906102be6109f0565b506102c76109f0565b926001600160801b0319809316938486527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfd835281862093845461031261030d82610a13565b6109ca565b818152601f1996908761032484610a13565b018a5b8181106108c65750508452885b828110610834575050508587527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c06845282872090815461037661030d82610a13565b928184528761038483610a13565b018a5b81811061080a575050868501938452895b8281106107a8575050508688527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c0085528388209687546103da61030d82610a13565b98818a52886103e883610a13565b018b5b81811061077d575050868601998a528a5b82811061070f5750505088527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c0385528388209384549461043e61030d87610a13565b908682528861044c88610a13565b018b5b8181106106d05750506060968787019283528b5b818110610616575050825188815295516080878a01819052815160a0808a01829052989c60c08e019b9a9998929796959450928a01908e5b8181106105e55750505051988a8c820301848d015288808b5192838152019a01908d5b81811061059c575050505197898b820301878c015287808a5192838152019901908c5b81811061055557505050519789880301838a0152858089519889815201980199945b878610610510578989038af35b8a51805184168a52808801518a8901528083015164ffffffffff168a8401528082015184168a8301528401518316898501529986019997840197600190950194610503565b90919a9c98998a85826105868f946001959d999a9b9c9d51602080916001600160801b031981511684520151910152565b9b9f9d9b019b9a959998979695019291016104e1565b90919b9d999a8b86828f6001946105cd919e9a9b9c9d9e51602080916001600160801b031981511684520151910152565b019d0191019d9b9a999d9897969594989190916104be565b8251805189168e52808d015189168e8e015280880151888f01528b01518b8e01529b88019b918b019160010161049b565b806106bd81859f9b96959d9e9d8d898b8f6106356106c399849661091b565b9390549183600393608096851b1c861b1681527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c04865220828c51966106786109aa565b96828454881b16885260018401549088015260028301549064ffffffffff82169088015260581b168d8601520154821b16908201526106b78383610a50565b52610a50565b50610a2b565b9b9192979b9a999a610463565b89906106e29d9994939d9c9b9c6109aa565b8a81528a838201528a878201528a60608201528a6080820152828286010152019b9192979b9a999a61044f565b806106bd818d8b9f9b9d9e9d610768907f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c018f8f8d6107506107729b8d61091b565b90549060031b1c60801b169052528d20915191610a64565b6106b78383610a50565b9a969a9998996103fc565b808c8b9a9e9a80939d9e9d61079061098a565b928d84528d83850152010152019b979b9a999a6103eb565b80846107bd6107ff93859e9a9e9d9c9d61091b565b90549060031b1c60801b1688527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c0789526106bd818d8a20610768895191610a64565b999599989798610398565b889061081a9c989c9b9a9b61098a565b8981528983820152828289010152019a969a999899610387565b806106bd81898760028b9f9b9d9e9d6108506108bb988a61091b565b93905460809460031b1c841b168d527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfe8452808d20908c51948c610892610954565b9581855480921b1687521690850152600182015490840152015460608201526106b78383610a50565b989498979697610334565b8890888d6108d89e9a9e9d9c9d610954565b918183528185840152820152896060820152828286010152019a969a999899610327565b80fd5b600435906001600160801b03198216820361091657565b600080fd5b919091805483101561093e57600052601060206000208360011c019260041b1690565b634e487b7160e01b600052603260045260246000fd5b604051906080820182811067ffffffffffffffff82111761097457604052565b634e487b7160e01b600052604160045260246000fd5b604051906040820182811067ffffffffffffffff82111761097457604052565b6040519060a0820182811067ffffffffffffffff82111761097457604052565b6040519190601f01601f1916820167ffffffffffffffff81118382101761097457604052565b6109f8610954565b90606082526060602083015260606040830152606080830152565b67ffffffffffffffff81116109745760051b60200190565b6000198114610a3a5760010190565b634e487b7160e01b600052601160045260246000fd5b805182101561093e5760209160051b010190565b906001610a6f61098a565b835460801b6001600160801b0319168152920154602083015256", - "sourceMap": "272:3088:71:-:0;;;;;;;;;;;;;;;;;", - "linkReferences": {} - }, - "deployedBytecode": { - "object": "0x604060808152600436101561001357600080fd5b600090813560e01c80636d6cf824146102965780639e35325f146101445763eafa8a5f1461004057600080fd5b346101405780600319360112610140576100586108ff565b6024356001600160a01b03811692919083810361013c576001600160a01b03811660009081527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c0960205260409020546001600160801b0319939060801b841661012557506001600160a01b031660009081527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c09602052604090208160801c83825416179055167fcb95900f20209676b1525cc13b65d7da43bed8372441da3bef171a88d91c54d68380a380f35b5163618ab76160e11b815260048101859052602490fd5b8480fd5b5080fd5b50346101405760203660031901126101405761015e6108ff565b6001600160801b03198116918284527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf791826020528185205461027e577f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf6908154906801000000000000000082101561026a5760018201808455821015610256578160011c7ff914494c1c9f5225b90a8394780d36857ab4e0eda6db8a41ed9ef9647838916501916001600160801b03608084549260071b169260801c831b921b191617905554918385526020528320557f53df47344d1cdf2ddb4901af5df61e37e14606bb7c8cc004d65c7c83ab3d06938280a280f35b634e487b7160e01b87526032600452602487fd5b634e487b7160e01b87526041600452602487fd5b81516333270c6160e01b815260048101859052602490fd5b5090346108fc57602090816003193601126108fc5791906102b56108ff565b906102be6109f0565b506102c76109f0565b926001600160801b0319809316938486527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfd835281862093845461031261030d82610a13565b6109ca565b818152601f1996908761032484610a13565b018a5b8181106108c65750508452885b828110610834575050508587527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c06845282872090815461037661030d82610a13565b928184528761038483610a13565b018a5b81811061080a575050868501938452895b8281106107a8575050508688527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c0085528388209687546103da61030d82610a13565b98818a52886103e883610a13565b018b5b81811061077d575050868601998a528a5b82811061070f5750505088527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c0385528388209384549461043e61030d87610a13565b908682528861044c88610a13565b018b5b8181106106d05750506060968787019283528b5b818110610616575050825188815295516080878a01819052815160a0808a01829052989c60c08e019b9a9998929796959450928a01908e5b8181106105e55750505051988a8c820301848d015288808b5192838152019a01908d5b81811061059c575050505197898b820301878c015287808a5192838152019901908c5b81811061055557505050519789880301838a0152858089519889815201980199945b878610610510578989038af35b8a51805184168a52808801518a8901528083015164ffffffffff168a8401528082015184168a8301528401518316898501529986019997840197600190950194610503565b90919a9c98998a85826105868f946001959d999a9b9c9d51602080916001600160801b031981511684520151910152565b9b9f9d9b019b9a959998979695019291016104e1565b90919b9d999a8b86828f6001946105cd919e9a9b9c9d9e51602080916001600160801b031981511684520151910152565b019d0191019d9b9a999d9897969594989190916104be565b8251805189168e52808d015189168e8e015280880151888f01528b01518b8e01529b88019b918b019160010161049b565b806106bd81859f9b96959d9e9d8d898b8f6106356106c399849661091b565b9390549183600393608096851b1c861b1681527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c04865220828c51966106786109aa565b96828454881b16885260018401549088015260028301549064ffffffffff82169088015260581b168d8601520154821b16908201526106b78383610a50565b52610a50565b50610a2b565b9b9192979b9a999a610463565b89906106e29d9994939d9c9b9c6109aa565b8a81528a838201528a878201528a60608201528a6080820152828286010152019b9192979b9a999a61044f565b806106bd818d8b9f9b9d9e9d610768907f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c018f8f8d6107506107729b8d61091b565b90549060031b1c60801b169052528d20915191610a64565b6106b78383610a50565b9a969a9998996103fc565b808c8b9a9e9a80939d9e9d61079061098a565b928d84528d83850152010152019b979b9a999a6103eb565b80846107bd6107ff93859e9a9e9d9c9d61091b565b90549060031b1c60801b1688527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c0789526106bd818d8a20610768895191610a64565b999599989798610398565b889061081a9c989c9b9a9b61098a565b8981528983820152828289010152019a969a999899610387565b806106bd81898760028b9f9b9d9e9d6108506108bb988a61091b565b93905460809460031b1c841b168d527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfe8452808d20908c51948c610892610954565b9581855480921b1687521690850152600182015490840152015460608201526106b78383610a50565b989498979697610334565b8890888d6108d89e9a9e9d9c9d610954565b918183528185840152820152896060820152828286010152019a969a999899610327565b80fd5b600435906001600160801b03198216820361091657565b600080fd5b919091805483101561093e57600052601060206000208360011c019260041b1690565b634e487b7160e01b600052603260045260246000fd5b604051906080820182811067ffffffffffffffff82111761097457604052565b634e487b7160e01b600052604160045260246000fd5b604051906040820182811067ffffffffffffffff82111761097457604052565b6040519060a0820182811067ffffffffffffffff82111761097457604052565b6040519190601f01601f1916820167ffffffffffffffff81118382101761097457604052565b6109f8610954565b90606082526060602083015260606040830152606080830152565b67ffffffffffffffff81116109745760051b60200190565b6000198114610a3a5760010190565b634e487b7160e01b600052601160045260246000fd5b805182101561093e5760209160051b010190565b906001610a6f61098a565b835460801b6001600160801b0319168152920154602083015256", - "sourceMap": "272:3088:71:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;-1:-1:-1;;;;;272:3088:71;;;;;;;;;;-1:-1:-1;;;;;272:3088:71;;;;;;1117:25;272:3088;;;;;;-1:-1:-1;;;;;;272:3088:71;;;;;;1113:129;;-1:-1:-1;;;;;;272:3088:71;;;;;1117:25;272:3088;;;;;;;;;;;;;;;;1372:56;;;;272:3088;;1113:129;272:3088;-1:-1:-1;;;1195:36:71;;272:3088;1195:36;;272:3088;;;;;1195:36;272:3088;;;;;;;;;;;;;;;-1:-1:-1;;272:3088:71;;;;;;:::i;:::-;-1:-1:-1;;;;;272:3088:71;;;;;;;683:19;272:3088;;;;;;;;679:95;;784:15;272:3088;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;272:3088:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;884:23;;;;272:3088;;;-1:-1:-1;;;272:3088:71;;;;;;;;;-1:-1:-1;;;272:3088:71;;;;;;;;679:95;272:3088;;-1:-1:-1;;;734:29:71;;272:3088;734:29;;272:3088;;;;;734:29;272:3088;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;:::i;:::-;;-1:-1:-1;;;;;272:3088:71;;;;;;;;1719:23;272:3088;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;-1:-1:-1;;272:3088:71;;;;;;:::i;:::-;;;;;;;;;1792:68;;;;1875:10;1887:26;;;;;;272:3088;;;;;;2098:25;272:3088;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;;2173:18;;;;;:74;;;2262:10;2274:28;;;;;;272:3088;;;;;;2501:29;272:3088;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;;2580:22;;;;;:86;;;2681:10;2693:32;;;;;;272:3088;;;;;2943:36;272:3088;;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;;;;;3029:29;;;;:99;;;3143:10;3155:31;;;;;;-1:-1:-1;;272:3088:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;272:3088:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1956:34;272:3088;;;;;;;;;;;;;;;;;;;1956:34;272:3088;;;;;;;;;;;-1:-1:-1;;;;;272:3088:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1956:34;272:3088;;;;;;;;;;;;;-1:-1:-1;;;;;272:3088:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1956:34;272:3088;;;3188:3;3290:23;3207:107;3290:23;;;;;;;;;;;;;;3188:3;3290:23;;;;:::i;:::-;272:3088;;;;;;;;;;;;;;;;;3242:47;272:3088;;;3207:29;;;272:3088;;;:::i;:::-;;;;;;;;;;1956:34;272:3088;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3207:107;;;;:::i;:::-;;;:::i;:::-;;3188:3;:::i;:::-;3143:10;;;;;;;;;;272:3088;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2727:3;2815:24;2746:94;2815:24;;;;;;;;272:3088;2815:24;2774:40;2815:24;;;;2727:3;2815:24;;;:::i;:::-;272:3088;;;;;;;;;;;;;;2746:22;;272:3088;;:::i;:::-;2746:94;;;;:::i;2727:3::-;2681:10;;;;;;;;272:3088;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;2304:3;2384:20;;;2304:3;2384:20;;;;;;;;;:::i;:::-;272:3088;;;;;;;;;;;2347:36;272:3088;;2323:82;272:3088;;;;;2323:18;;272:3088;;:::i;2304:3::-;2262:10;;;;;;;;272:3088;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;1915:3;1991:18;1934:76;1991:18;;;272:3088;1991:18;;;;;;;1915:3;1991:18;;;:::i;:::-;272:3088;;;;;;;;;;;;;1956:34;272:3088;;;;;1934:16;;;272:3088;;;;:::i;:::-;;;;;;;;;;;;;;;;1956:34;272:3088;;;;;;;;;;;;;1934:76;;;;:::i;1915:3::-;1875:10;;;;;;;;272:3088;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;272:3088:71;;;;;;:::o;:::-;;;;;;;;;;;;;;;-1:-1:-1;272:3088:71;;;-1:-1:-1;272:3088:71;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;-1:-1:-1;;272:3088:71;;;;;;;;;;;;;;:::o;:::-;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;-1:-1:-1;;272:3088:71;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;:::i;:::-;;;;;-1:-1:-1;;;;;;272:3088:71;;;;;;;;;;:::o", - "linkReferences": {} - }, - "methodIdentifiers": { - "createStakeholder(bytes16)": "9e35325f", - "getStakeholderPositions(bytes16)": "6d6cf824", - "linkStakeholderAddress(bytes16,address)": "eafa8a5f" - }, - "rawMetadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"wallet_address\",\"type\":\"address\"}],\"name\":\"AddressAlreadyLinked\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"}],\"name\":\"StakeholderAlreadyExists\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"wallet_address\",\"type\":\"address\"}],\"name\":\"StakeholderAddressLinked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes16\",\"name\":\"id\",\"type\":\"bytes16\"}],\"name\":\"StakeholderCreated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"_id\",\"type\":\"bytes16\"}],\"name\":\"createStakeholder\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"}],\"name\":\"getStakeholderPositions\",\"outputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"},{\"internalType\":\"bytes16\",\"name\":\"stock_class_id\",\"type\":\"bytes16\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"share_price\",\"type\":\"uint256\"}],\"internalType\":\"struct StockActivePosition[]\",\"name\":\"stocks\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct WarrantActivePosition[]\",\"name\":\"warrants\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"},{\"internalType\":\"uint256\",\"name\":\"investment_amount\",\"type\":\"uint256\"}],\"internalType\":\"struct ConvertibleActivePosition[]\",\"name\":\"convertibles\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"},{\"internalType\":\"uint40\",\"name\":\"timestamp\",\"type\":\"uint40\"},{\"internalType\":\"bytes16\",\"name\":\"stock_class_id\",\"type\":\"bytes16\"},{\"internalType\":\"bytes16\",\"name\":\"stock_plan_id\",\"type\":\"bytes16\"}],\"internalType\":\"struct EquityCompensationActivePosition[]\",\"name\":\"equityCompensations\",\"type\":\"tuple[]\"}],\"internalType\":\"struct StakeholderPositions\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"},{\"internalType\":\"address\",\"name\":\"wallet_address\",\"type\":\"address\"}],\"name\":\"linkStakeholderAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/lib/diamond/facets/StakeholderFacet.sol\":\"StakeholderFacet\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"appendCBOR\":false,\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@diamond/=src/lib/diamond/\",\":diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/\",\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\",\":openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/\"],\"viaIR\":true},\"sources\":{\"src/lib/diamond/Storage.sol\":{\"keccak256\":\"0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d\",\"dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL\"]},\"src/lib/diamond/Structs.sol\":{\"keccak256\":\"0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab\",\"dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD\"]},\"src/lib/diamond/facets/StakeholderFacet.sol\":{\"keccak256\":\"0x1d636a399b0bcbe8fa4115b6bd13b09cbc4dfaf73ce862a98419100ddeee56e4\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5748c73c6a7422af0b198ea6c150d21ce5a77413292f55c68543f4a8da8255b9\",\"dweb:/ipfs/Qma642YoBMLAdmxPKhks2ki8j6UmCQiH2vyQirJaHXz5eZ\"]}},\"version\":1}", - "metadata": { - "compiler": { - "version": "0.8.20+commit.a1b79de6" - }, - "language": "Solidity", - "output": { - "abi": [ - { - "inputs": [ - { - "internalType": "address", - "name": "wallet_address", - "type": "address" - } - ], - "type": "error", - "name": "AddressAlreadyLinked" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - } - ], - "type": "error", - "name": "StakeholderAlreadyExists" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16", - "indexed": true - }, - { - "internalType": "address", - "name": "wallet_address", - "type": "address", - "indexed": true - } - ], - "type": "event", - "name": "StakeholderAddressLinked", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "id", - "type": "bytes16", - "indexed": true - } - ], - "type": "event", - "name": "StakeholderCreated", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "_id", - "type": "bytes16" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "createStakeholder" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - } - ], - "stateMutability": "view", - "type": "function", - "name": "getStakeholderPositions", - "outputs": [ - { - "internalType": "struct StakeholderPositions", - "name": "", - "type": "tuple", - "components": [ - { - "internalType": "struct StockActivePosition[]", - "name": "stocks", - "type": "tuple[]", - "components": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - }, - { - "internalType": "bytes16", - "name": "stock_class_id", - "type": "bytes16" - }, - { - "internalType": "uint256", - "name": "quantity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "share_price", - "type": "uint256" - } - ] - }, - { - "internalType": "struct WarrantActivePosition[]", - "name": "warrants", - "type": "tuple[]", - "components": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - }, - { - "internalType": "uint256", - "name": "quantity", - "type": "uint256" - } - ] - }, - { - "internalType": "struct ConvertibleActivePosition[]", - "name": "convertibles", - "type": "tuple[]", - "components": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - }, - { - "internalType": "uint256", - "name": "investment_amount", - "type": "uint256" - } - ] - }, - { - "internalType": "struct EquityCompensationActivePosition[]", - "name": "equityCompensations", - "type": "tuple[]", - "components": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - }, - { - "internalType": "uint256", - "name": "quantity", - "type": "uint256" - }, - { - "internalType": "uint40", - "name": "timestamp", - "type": "uint40" - }, - { - "internalType": "bytes16", - "name": "stock_class_id", - "type": "bytes16" - }, - { - "internalType": "bytes16", - "name": "stock_plan_id", - "type": "bytes16" - } - ] - } - ] - } - ] - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - }, - { - "internalType": "address", - "name": "wallet_address", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "linkStakeholderAddress" - } - ], - "devdoc": { - "kind": "dev", - "methods": {}, - "version": 1 - }, - "userdoc": { - "kind": "user", - "methods": {}, - "version": 1 - } - }, - "settings": { - "remappings": [ - "@diamond/=src/lib/diamond/", - "diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/", - "ds-test/=lib/forge-std/lib/ds-test/src/", - "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", - "forge-std/=lib/forge-std/src/", - "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", - "openzeppelin-contracts/=lib/openzeppelin-contracts/", - "openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/" - ], - "optimizer": { - "enabled": true, - "runs": 200 - }, - "metadata": { - "bytecodeHash": "none", - "appendCBOR": false - }, - "compilationTarget": { - "src/lib/diamond/facets/StakeholderFacet.sol": "StakeholderFacet" - }, - "evmVersion": "paris", - "libraries": {}, - "viaIR": true - }, - "sources": { - "src/lib/diamond/Storage.sol": { - "keccak256": "0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f", - "urls": [ - "bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d", - "dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL" - ], - "license": "MIT" - }, - "src/lib/diamond/Structs.sol": { - "keccak256": "0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2", - "urls": [ - "bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab", - "dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/StakeholderFacet.sol": { - "keccak256": "0x1d636a399b0bcbe8fa4115b6bd13b09cbc4dfaf73ce862a98419100ddeee56e4", - "urls": [ - "bzz-raw://5748c73c6a7422af0b198ea6c150d21ce5a77413292f55c68543f4a8da8255b9", - "dweb:/ipfs/Qma642YoBMLAdmxPKhks2ki8j6UmCQiH2vyQirJaHXz5eZ" - ], - "license": "MIT" - } - }, - "version": 1 - }, - "id": 71 -} \ No newline at end of file diff --git a/chain/out/StakeholderNFTFacet.sol/StakeholderNFTFacet.json b/chain/out/StakeholderNFTFacet.sol/StakeholderNFTFacet.json deleted file mode 100644 index ecc50c78..00000000 --- a/chain/out/StakeholderNFTFacet.sol/StakeholderNFTFacet.json +++ /dev/null @@ -1,965 +0,0 @@ -{ - "abi": [ - { - "type": "constructor", - "inputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "approve", - "inputs": [ - { - "name": "to", - "type": "address", - "internalType": "address" - }, - { - "name": "tokenId", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "balanceOf", - "inputs": [ - { - "name": "owner", - "type": "address", - "internalType": "address" - } - ], - "outputs": [ - { - "name": "", - "type": "uint256", - "internalType": "uint256" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "getApproved", - "inputs": [ - { - "name": "tokenId", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "isApprovedForAll", - "inputs": [ - { - "name": "owner", - "type": "address", - "internalType": "address" - }, - { - "name": "operator", - "type": "address", - "internalType": "address" - } - ], - "outputs": [ - { - "name": "", - "type": "bool", - "internalType": "bool" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "mint", - "inputs": [], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "name", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "string", - "internalType": "string" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "ownerOf", - "inputs": [ - { - "name": "tokenId", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "safeTransferFrom", - "inputs": [ - { - "name": "from", - "type": "address", - "internalType": "address" - }, - { - "name": "to", - "type": "address", - "internalType": "address" - }, - { - "name": "tokenId", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "safeTransferFrom", - "inputs": [ - { - "name": "from", - "type": "address", - "internalType": "address" - }, - { - "name": "to", - "type": "address", - "internalType": "address" - }, - { - "name": "tokenId", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "data", - "type": "bytes", - "internalType": "bytes" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "setApprovalForAll", - "inputs": [ - { - "name": "operator", - "type": "address", - "internalType": "address" - }, - { - "name": "approved", - "type": "bool", - "internalType": "bool" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "supportsInterface", - "inputs": [ - { - "name": "interfaceId", - "type": "bytes4", - "internalType": "bytes4" - } - ], - "outputs": [ - { - "name": "", - "type": "bool", - "internalType": "bool" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "symbol", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "string", - "internalType": "string" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "tokenURI", - "inputs": [ - { - "name": "tokenId", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [ - { - "name": "", - "type": "string", - "internalType": "string" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "transferFrom", - "inputs": [ - { - "name": "from", - "type": "address", - "internalType": "address" - }, - { - "name": "to", - "type": "address", - "internalType": "address" - }, - { - "name": "tokenId", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "event", - "name": "Approval", - "inputs": [ - { - "name": "owner", - "type": "address", - "indexed": true, - "internalType": "address" - }, - { - "name": "approved", - "type": "address", - "indexed": true, - "internalType": "address" - }, - { - "name": "tokenId", - "type": "uint256", - "indexed": true, - "internalType": "uint256" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "ApprovalForAll", - "inputs": [ - { - "name": "owner", - "type": "address", - "indexed": true, - "internalType": "address" - }, - { - "name": "operator", - "type": "address", - "indexed": true, - "internalType": "address" - }, - { - "name": "approved", - "type": "bool", - "indexed": false, - "internalType": "bool" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "Transfer", - "inputs": [ - { - "name": "from", - "type": "address", - "indexed": true, - "internalType": "address" - }, - { - "name": "to", - "type": "address", - "indexed": true, - "internalType": "address" - }, - { - "name": "tokenId", - "type": "uint256", - "indexed": true, - "internalType": "uint256" - } - ], - "anonymous": false - }, - { - "type": "error", - "name": "AlreadyMinted", - "inputs": [] - }, - { - "type": "error", - "name": "NotStakeholder", - "inputs": [] - }, - { - "type": "error", - "name": "URIQueryForNonexistentToken", - "inputs": [] - } - ], - "bytecode": { - "object": "0x60803462000323576001600160401b0390604090808201838111828210176200030d578252601481526020927f5374616b65686f6c64657220506f736974696f6e000000000000000000000000848301528251838101818110838211176200030d578452600681526553544b504f5360d01b858201528251908282116200030d5760008054926001958685811c9516801562000302575b89861014620002ee578190601f958681116200029b575b508990868311600114620002375784926200022b575b5050600019600383901b1c191690861b1781555b8151938411620002175784548581811c911680156200020c575b88821014620001f857838111620001b0575b50869284116001146200014a578394959650926200013e575b5050600019600383901b1c191690821b1790555b51611b759081620003298239f35b0151905038806200011c565b9190601f1984169685845280842093905b8882106200019857505083859697106200017e575b505050811b01905562000130565b015160001960f88460031b161c1916905538808062000170565b8087859682949686015181550195019301906200015b565b8582528782208480870160051c8201928a8810620001ee575b0160051c019086905b828110620001e257505062000103565b838155018690620001d2565b92508192620001c9565b634e487b7160e01b82526022600452602482fd5b90607f1690620000f1565b634e487b7160e01b81526041600452602490fd5b015190503880620000c3565b8480528a85208994509190601f198416865b8d8282106200028457505084116200026a575b505050811b018155620000d7565b015160001960f88460031b161c191690553880806200025c565b8385015186558c9790950194938401930162000249565b9091508380528984208680850160051c8201928c8610620002e4575b918a91869594930160051c01915b828110620002d5575050620000ad565b8681558594508a9101620002c5565b92508192620002b7565b634e487b7160e01b83526022600452602483fd5b94607f169462000096565b634e487b7160e01b600052604160045260246000fd5b600080fdfe608080604052600436101561001357600080fd5b60003560e01c90816301ffc9a714610e185750806306fdde0314610d59578063081812fc14610d3b578063095ea7b314610bc15780631249c58b14610a0357806323b872dd146109da57806342842e0e146109b25780636352211e1461098257806370a08231146108eb57806395d89b41146107e0578063a22cb4651461070e578063b88d4fde14610684578063c87b56dd146101145763e985e9c5146100b957600080fd5b3461010f57604036600319011261010f576100d2610ecb565b6100da610ee1565b9060018060a01b03809116600052600560205260406000209116600052602052602060ff604060002054166040519015158152f35b600080fd5b3461010f57602036600319011261010f576004356000908152600260205260409020546001600160a01b03161561067257604051631b5b3e0960e21b81526004803560801b6001600160801b03191690820152600081602481305afa908115610666576000916103c2575b6103be610357610339610352609e86610199600435611a84565b906101a481516116f9565b9061026a60256101b760208401516117d0565b926101d260606101ca60408401516118ab565b92015161198e565b6040519485926020840197605b60f81b89526101f8815180926020602189019101610e83565b840191600b60fa1b9283602182015261021b825180936020602285019101610e83565b01826022820152610236825180936020602385019101610e83565b01906023820152610251825180936020602485019101610e83565b01605d60f81b6024820152036005810184520182610f7d565b6040519586937f7b226e616d65223a225374616b65686f6c64657220506f736974696f6e20230060208601526102aa815180926020603f89019101610e83565b7f222c226465736372697074696f6e223a2254686973204e465420726570726573603f918601918201527f656e747320616c6c2061637469766520706f736974696f6e7320666f72207468605f820152701a5cc81cdd185ad95a1bdb19195c8b888b607a1b607f8201526c1130ba3a3934b13aba32b9911d60991b60908201529151928390609d840190610e83565b01607d60f81b609d82015203607e810184520182610f7d565b61157c565b6103aa603d60405180937f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c000000602083015261039a8151809260208686019101610e83565b810103601d810184520182610f7d565b604051918291602083526020830190610ea6565b0390f35b3d9150816000823e6103d48282610f7d565b602081838101031261010f5780516001600160401b03811161010f57810191608083828401031261010f576040519261040c84610f2c565b80516001600160401b03811161010f578101828401601f8201121561010f578051610436816114b1565b916104446040519384610f7d565b81835260208084019260071b82010190858701821161010f57602001915b81831061061057505050845260208101516001600160401b03811161010f578101828401601f8201121561010f576104a390838501906020815191016114dd565b602085015260408101516001600160401b03811161010f578101828401601f8201121561010f576104dd90838501906020815191016114dd565b60408501526060810151906001600160401b03821161010f570190808301601f8301121561010f57815190610511826114b1565b9361051f6040519586610f7d565b8285526020850193828201602060a086028301011161010f579360208501945b602060a08602820101861061056457505050505060608301919091525061035761017f565b60a086858501031261010f57604051918260a08101106001600160401b0360a0850111176105fa5760a0830160405261059c876114c8565b83526020870151602084015260408701519264ffffffffff8416840361010f57602060a0928282966040869501526105d660608c016114c8565b60608201526105e760808c016114c8565b608082015281520197019692505061053f565b634e487b7160e01b600052604160045260246000fd5b608083878901031261010f57602060809160405161062d81610f2c565b610636866114c8565b81526106438387016114c8565b838201526040860151604082015260608087015190820152815201920191610462565b6040513d6000823e3d90fd5b604051630a14c4b560e41b8152600490fd5b3461010f57608036600319011261010f5761069d610ecb565b6106a5610ee1565b606435916001600160401b03831161010f573660238401121561010f578260040135916106d183610f9e565b926106df6040519485610f7d565b808452366024828701011161010f57602081600092602461070c98018388013785010152604435916110cb565b005b3461010f57604036600319011261010f57610727610ecb565b6024359081151580920361010f576001600160a01b03169033821461079b57336000526005602052604060002082600052602052604060002060ff1981541660ff83161790556040519081527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b60405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606490fd5b3461010f57600036600319011261010f57604051600060019081549182811c918184169182156108e1575b60209485851084146108cb5785879486865291826000146108ab575050600114610851575b5061083d92500383610f7d565b6103be604051928284938452830190610ea6565b6000818152859250907fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b85831061089357505061083d935082010185610830565b8054838901850152879450869390920191810161087c565b60ff19168582015261083d95151560051b85010192508791506108309050565b634e487b7160e01b600052602260045260246000fd5b92607f169261080b565b3461010f57602036600319011261010f576001600160a01b0361090c610ecb565b16801561092b5760005260036020526020604060002054604051908152f35b60405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b6064820152608490fd5b3461010f57602036600319011261010f5760206109a0600435611005565b6040516001600160a01b039091168152f35b3461010f5761070c6109c336610ef7565b90604051926109d184610f47565b600084526110cb565b3461010f5761070c6109eb36610ef7565b916109fe6109f98433611166565b611069565b61122e565b3461010f57600036600319011261010f573360005260207f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c0981526fffffffffffffffffffffffffffffffff1960406000205460801b1690816000527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf7815260406000205415610baf576000828152600260205260409020546001600160a01b0316610b9d573315610b5a57600290610ad9610ad384600052600260205260018060a01b0360406000205416151590565b15611465565b600083815260026020526040902054610afc906001600160a01b03161515610ad3565b336000526003815260406000206001815401905582600052526040600020336bffffffffffffffffffffffff60a01b8254161790553360007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a4005b6064906040519062461bcd60e51b825280600483015260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152fd5b604051631bbdf5c560e31b8152600490fd5b604051637592296b60e01b8152600490fd5b3461010f57604036600319011261010f57610bda610ecb565b602435906001600160a01b038080610bf185611005565b16921691808314610cec57803314908115610cc7575b5015610c5c57600083815260046020526040902080546001600160a01b03191683179055610c3483611005565b167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600080a4005b60405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c0000006064820152608490fd5b9050600052600560205260406000203360005260205260ff6040600020541684610c07565b60405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608490fd5b3461010f57602036600319011261010f5760206109a060043561102b565b3461010f57600036600319011261010f576040516000805490600182811c91818416918215610e0e575b60209485851084146108cb5785879486865291826000146108ab575050600114610db4575061083d92500383610f7d565b6000808052859250907f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5635b858310610df657505061083d935082010185610830565b80548389018501528794508693909201918101610ddf565b92607f1692610d83565b3461010f57602036600319011261010f576004359063ffffffff60e01b821680920361010f576020916380ac58cd60e01b8114908115610e72575b8115610e61575b5015158152f35b6301ffc9a760e01b14905083610e5a565b635b5e139f60e01b81149150610e53565b60005b838110610e965750506000910152565b8181015183820152602001610e86565b90602091610ebf81518092818552858086019101610e83565b601f01601f1916010190565b600435906001600160a01b038216820361010f57565b602435906001600160a01b038216820361010f57565b606090600319011261010f576001600160a01b0390600435828116810361010f5791602435908116810361010f579060443590565b608081019081106001600160401b038211176105fa57604052565b602081019081106001600160401b038211176105fa57604052565b606081019081106001600160401b038211176105fa57604052565b90601f801991011681019081106001600160401b038211176105fa57604052565b6001600160401b0381116105fa57601f01601f191660200190565b15610fc057565b60405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e20494400000000000000006044820152606490fd5b6000908152600260205260409020546001600160a01b0316611028811515610fb9565b90565b60008181526002602052604090205461104e906001600160a01b03161515610fb9565b6000908152600460205260409020546001600160a01b031690565b1561107057565b60405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201526c1c881bdc88185c1c1c9bdd9959609a1b6064820152608490fd5b906110ef9392916110df6109f98433611166565b6110ea83838361122e565b611344565b156110f657565b60405162461bcd60e51b81528061110f60048201611113565b0390fd5b60809060208152603260208201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b60608201520190565b906001600160a01b03808061117a84611005565b169316918383149384156111ad575b508315611197575b50505090565b6111a39192935061102b565b1614388080611191565b909350600052600560205260406000208260005260205260ff604060002054169238611189565b156111db57565b60405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608490fd5b906112569161123c84611005565b6001600160a01b03939184169284929091831684146111d4565b169182156112f357816112739161126c86611005565b16146111d4565b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60008481526004602052604081206bffffffffffffffffffffffff60a01b9081815416905583825260036020526040822060001981540190558482526040822060018154019055858252600260205284604083209182541617905580a4565b60405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608490fd5b92939190803b1561145b57611396946040518092630a85bd0160e11b9485835233600484015260018060a01b03809816602484015260448301526080606483015281806020998a956084830190610ea6565b03916000988991165af1849181611417575b50611406575050503d6000146113fe573d6113c281610f9e565b906113d06040519283610f7d565b81528091833d92013e5b805191826113fb5760405162461bcd60e51b81528061110f60048201611113565b01fd5b5060606113da565b6001600160e01b0319161492509050565b9091508581813d8311611454575b61142f8183610f7d565b8101031261145057516001600160e01b0319811681036114505790386113a8565b8480fd5b503d611425565b5050915050600190565b1561146c57565b60405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606490fd5b6001600160401b0381116105fa5760051b60200190565b51906001600160801b03198216820361010f57565b9291926114e9826114b1565b6040926114f884519283610f7d565b819581835260208093019160061b84019381851161010f57915b84831061152157505050505050565b858383031261010f57855190868201908282106001600160401b0383111761156757879286928452611552866114c8565b81528286015183820152815201920191611512565b60246000634e487b7160e01b81526041600452fd5b8051156116e55760405161158f81610f62565b604081527f4142434445464748494a4b4c4d4e4f505152535455565758595a61626364656660208201527f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f604082015281516002928382018092116116cf5760039182900480851b94906001600160fe1b038116036116cf579261162b61161586610f9e565b956116236040519788610f7d565b808752610f9e565b6020860190601f190136823793829183518401925b83811061167e575050505051068060011461166b57600214611660575090565b603d90600019015390565b50603d9081600019820153600119015390565b85600491979293949701918251600190603f9082828260121c16880101518453828282600c1c16880101518385015382828260061c1688010151888501531685010151878201530195929190611640565b634e487b7160e01b600052601160045260246000fd5b506040516116f281610f47565b6000815290565b80511561177e5761170a9051611a84565b611028604e60405180937f7b2274726169745f74797065223a202253746f636b20506f736974696f6e732260208301526b1610113b30b63ab2911d101160a11b6040830152611763815180926020604c86019101610e83565b810161227d60f01b604c82015203602e810184520182610f7d565b5060405161178b81610f62565b602f81527f7b2274726169745f74797065223a202253746f636b20506f736974696f6e732260208201526e2c202276616c7565223a202230227d60881b604082015290565b805115611857576117e19051611a84565b611028605060405180937f7b2274726169745f74797065223a202257617272616e7420506f736974696f6e60208301526d39911610113b30b63ab2911d101160911b604083015261183c815180926020604e86019101610e83565b810161227d60f01b604e820152036030810184520182610f7d565b5060405161186481610f62565b603181527f7b2274726169745f74797065223a202257617272616e7420506f736974696f6e60208201527073222c202276616c7565223a202230227d60781b604082015290565b805115611936576118bc9051611a84565b611028605460405180937f7b2274726169745f74797065223a2022436f6e7665727469626c6520506f73696020830152713a34b7b739911610113b30b63ab2911d101160711b604083015261191b815180926020605286019101610e83565b810161227d60f01b6052820152036034810184520182610f7d565b5060405161194381610f62565b603581527f7b2274726169745f74797065223a2022436f6e7665727469626c6520506f736960208201527474696f6e73222c202276616c7565223a202230227d60581b604082015290565b805115611a245761199f9051611a84565b611028605c60405180937f7b2274726169745f74797065223a202245717569747920436f6d70656e73617460208301527f696f6e20506f736974696f6e73222c202276616c7565223a20220000000000006040830152611a09815180926020605a86019101610e83565b810161227d60f01b605a82015203603c810184520182610f7d565b50604051611a3181610f62565b603d81527f7b2274726169745f74797065223a202245717569747920436f6d70656e73617460208201527f696f6e20506f736974696f6e73222c202276616c7565223a202230227d000000604082015290565b8015611b475780816000925b611b2d5750611a9e82610f9e565b91611aac6040519384610f7d565b808352601f19611abb82610f9e565b01908260209236848701375b611ad15750505090565b60001981019081116116cf578092600a91603083830681018091116116cf578651821015611b175760f81b6001600160f81b03191660001a908601840153049182611ac7565b634e487b7160e01b600052603260045260246000fd5b909160001981146116cf576001019190600a900480611a90565b50604051604081018181106001600160401b038211176105fa5760405260018152600360fc1b60208201529056", - "sourceMap": "522:4539:72:-:0;;;;-1:-1:-1;;;;;522:4539:72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;522:4539:72;;;;;;;;;;;;-1:-1:-1;522:4539:72;;;;;;;;;;;;;;;-1:-1:-1;522:4539:72;;;;;;;;;;;;;;;-1:-1:-1;522:4539:72;;;;;;;;;;;;;;;-1:-1:-1;;;;522:4539:72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;522:4539:72;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;522:4539:72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;522:4539:72;;;;;;;;;;;;;;;;-1:-1:-1;;;522:4539:72;;;;;;;;;;;;-1:-1:-1;522:4539:72;;;;;;;;;;;;;-1:-1:-1;522:4539:72;;-1:-1:-1;;522:4539:72;;;;;;;;;;;;;-1:-1:-1;522:4539:72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;522:4539:72;;;;;;;;;;;;;-1:-1:-1;;;522:4539:72;;;;;;;;;;;;;;;;;;;-1:-1:-1;522:4539:72;;;;;-1:-1:-1;522:4539:72;;;;", - "linkReferences": {} - }, - "deployedBytecode": { - "object": "0x608080604052600436101561001357600080fd5b60003560e01c90816301ffc9a714610e185750806306fdde0314610d59578063081812fc14610d3b578063095ea7b314610bc15780631249c58b14610a0357806323b872dd146109da57806342842e0e146109b25780636352211e1461098257806370a08231146108eb57806395d89b41146107e0578063a22cb4651461070e578063b88d4fde14610684578063c87b56dd146101145763e985e9c5146100b957600080fd5b3461010f57604036600319011261010f576100d2610ecb565b6100da610ee1565b9060018060a01b03809116600052600560205260406000209116600052602052602060ff604060002054166040519015158152f35b600080fd5b3461010f57602036600319011261010f576004356000908152600260205260409020546001600160a01b03161561067257604051631b5b3e0960e21b81526004803560801b6001600160801b03191690820152600081602481305afa908115610666576000916103c2575b6103be610357610339610352609e86610199600435611a84565b906101a481516116f9565b9061026a60256101b760208401516117d0565b926101d260606101ca60408401516118ab565b92015161198e565b6040519485926020840197605b60f81b89526101f8815180926020602189019101610e83565b840191600b60fa1b9283602182015261021b825180936020602285019101610e83565b01826022820152610236825180936020602385019101610e83565b01906023820152610251825180936020602485019101610e83565b01605d60f81b6024820152036005810184520182610f7d565b6040519586937f7b226e616d65223a225374616b65686f6c64657220506f736974696f6e20230060208601526102aa815180926020603f89019101610e83565b7f222c226465736372697074696f6e223a2254686973204e465420726570726573603f918601918201527f656e747320616c6c2061637469766520706f736974696f6e7320666f72207468605f820152701a5cc81cdd185ad95a1bdb19195c8b888b607a1b607f8201526c1130ba3a3934b13aba32b9911d60991b60908201529151928390609d840190610e83565b01607d60f81b609d82015203607e810184520182610f7d565b61157c565b6103aa603d60405180937f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c000000602083015261039a8151809260208686019101610e83565b810103601d810184520182610f7d565b604051918291602083526020830190610ea6565b0390f35b3d9150816000823e6103d48282610f7d565b602081838101031261010f5780516001600160401b03811161010f57810191608083828401031261010f576040519261040c84610f2c565b80516001600160401b03811161010f578101828401601f8201121561010f578051610436816114b1565b916104446040519384610f7d565b81835260208084019260071b82010190858701821161010f57602001915b81831061061057505050845260208101516001600160401b03811161010f578101828401601f8201121561010f576104a390838501906020815191016114dd565b602085015260408101516001600160401b03811161010f578101828401601f8201121561010f576104dd90838501906020815191016114dd565b60408501526060810151906001600160401b03821161010f570190808301601f8301121561010f57815190610511826114b1565b9361051f6040519586610f7d565b8285526020850193828201602060a086028301011161010f579360208501945b602060a08602820101861061056457505050505060608301919091525061035761017f565b60a086858501031261010f57604051918260a08101106001600160401b0360a0850111176105fa5760a0830160405261059c876114c8565b83526020870151602084015260408701519264ffffffffff8416840361010f57602060a0928282966040869501526105d660608c016114c8565b60608201526105e760808c016114c8565b608082015281520197019692505061053f565b634e487b7160e01b600052604160045260246000fd5b608083878901031261010f57602060809160405161062d81610f2c565b610636866114c8565b81526106438387016114c8565b838201526040860151604082015260608087015190820152815201920191610462565b6040513d6000823e3d90fd5b604051630a14c4b560e41b8152600490fd5b3461010f57608036600319011261010f5761069d610ecb565b6106a5610ee1565b606435916001600160401b03831161010f573660238401121561010f578260040135916106d183610f9e565b926106df6040519485610f7d565b808452366024828701011161010f57602081600092602461070c98018388013785010152604435916110cb565b005b3461010f57604036600319011261010f57610727610ecb565b6024359081151580920361010f576001600160a01b03169033821461079b57336000526005602052604060002082600052602052604060002060ff1981541660ff83161790556040519081527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b60405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606490fd5b3461010f57600036600319011261010f57604051600060019081549182811c918184169182156108e1575b60209485851084146108cb5785879486865291826000146108ab575050600114610851575b5061083d92500383610f7d565b6103be604051928284938452830190610ea6565b6000818152859250907fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b85831061089357505061083d935082010185610830565b8054838901850152879450869390920191810161087c565b60ff19168582015261083d95151560051b85010192508791506108309050565b634e487b7160e01b600052602260045260246000fd5b92607f169261080b565b3461010f57602036600319011261010f576001600160a01b0361090c610ecb565b16801561092b5760005260036020526020604060002054604051908152f35b60405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b6064820152608490fd5b3461010f57602036600319011261010f5760206109a0600435611005565b6040516001600160a01b039091168152f35b3461010f5761070c6109c336610ef7565b90604051926109d184610f47565b600084526110cb565b3461010f5761070c6109eb36610ef7565b916109fe6109f98433611166565b611069565b61122e565b3461010f57600036600319011261010f573360005260207f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c0981526fffffffffffffffffffffffffffffffff1960406000205460801b1690816000527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf7815260406000205415610baf576000828152600260205260409020546001600160a01b0316610b9d573315610b5a57600290610ad9610ad384600052600260205260018060a01b0360406000205416151590565b15611465565b600083815260026020526040902054610afc906001600160a01b03161515610ad3565b336000526003815260406000206001815401905582600052526040600020336bffffffffffffffffffffffff60a01b8254161790553360007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a4005b6064906040519062461bcd60e51b825280600483015260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152fd5b604051631bbdf5c560e31b8152600490fd5b604051637592296b60e01b8152600490fd5b3461010f57604036600319011261010f57610bda610ecb565b602435906001600160a01b038080610bf185611005565b16921691808314610cec57803314908115610cc7575b5015610c5c57600083815260046020526040902080546001600160a01b03191683179055610c3483611005565b167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600080a4005b60405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c0000006064820152608490fd5b9050600052600560205260406000203360005260205260ff6040600020541684610c07565b60405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608490fd5b3461010f57602036600319011261010f5760206109a060043561102b565b3461010f57600036600319011261010f576040516000805490600182811c91818416918215610e0e575b60209485851084146108cb5785879486865291826000146108ab575050600114610db4575061083d92500383610f7d565b6000808052859250907f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5635b858310610df657505061083d935082010185610830565b80548389018501528794508693909201918101610ddf565b92607f1692610d83565b3461010f57602036600319011261010f576004359063ffffffff60e01b821680920361010f576020916380ac58cd60e01b8114908115610e72575b8115610e61575b5015158152f35b6301ffc9a760e01b14905083610e5a565b635b5e139f60e01b81149150610e53565b60005b838110610e965750506000910152565b8181015183820152602001610e86565b90602091610ebf81518092818552858086019101610e83565b601f01601f1916010190565b600435906001600160a01b038216820361010f57565b602435906001600160a01b038216820361010f57565b606090600319011261010f576001600160a01b0390600435828116810361010f5791602435908116810361010f579060443590565b608081019081106001600160401b038211176105fa57604052565b602081019081106001600160401b038211176105fa57604052565b606081019081106001600160401b038211176105fa57604052565b90601f801991011681019081106001600160401b038211176105fa57604052565b6001600160401b0381116105fa57601f01601f191660200190565b15610fc057565b60405162461bcd60e51b815260206004820152601860248201527f4552433732313a20696e76616c696420746f6b656e20494400000000000000006044820152606490fd5b6000908152600260205260409020546001600160a01b0316611028811515610fb9565b90565b60008181526002602052604090205461104e906001600160a01b03161515610fb9565b6000908152600460205260409020546001600160a01b031690565b1561107057565b60405162461bcd60e51b815260206004820152602d60248201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560448201526c1c881bdc88185c1c1c9bdd9959609a1b6064820152608490fd5b906110ef9392916110df6109f98433611166565b6110ea83838361122e565b611344565b156110f657565b60405162461bcd60e51b81528061110f60048201611113565b0390fd5b60809060208152603260208201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b60608201520190565b906001600160a01b03808061117a84611005565b169316918383149384156111ad575b508315611197575b50505090565b6111a39192935061102b565b1614388080611191565b909350600052600560205260406000208260005260205260ff604060002054169238611189565b156111db57565b60405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608490fd5b906112569161123c84611005565b6001600160a01b03939184169284929091831684146111d4565b169182156112f357816112739161126c86611005565b16146111d4565b7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60008481526004602052604081206bffffffffffffffffffffffff60a01b9081815416905583825260036020526040822060001981540190558482526040822060018154019055858252600260205284604083209182541617905580a4565b60405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608490fd5b92939190803b1561145b57611396946040518092630a85bd0160e11b9485835233600484015260018060a01b03809816602484015260448301526080606483015281806020998a956084830190610ea6565b03916000988991165af1849181611417575b50611406575050503d6000146113fe573d6113c281610f9e565b906113d06040519283610f7d565b81528091833d92013e5b805191826113fb5760405162461bcd60e51b81528061110f60048201611113565b01fd5b5060606113da565b6001600160e01b0319161492509050565b9091508581813d8311611454575b61142f8183610f7d565b8101031261145057516001600160e01b0319811681036114505790386113a8565b8480fd5b503d611425565b5050915050600190565b1561146c57565b60405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606490fd5b6001600160401b0381116105fa5760051b60200190565b51906001600160801b03198216820361010f57565b9291926114e9826114b1565b6040926114f884519283610f7d565b819581835260208093019160061b84019381851161010f57915b84831061152157505050505050565b858383031261010f57855190868201908282106001600160401b0383111761156757879286928452611552866114c8565b81528286015183820152815201920191611512565b60246000634e487b7160e01b81526041600452fd5b8051156116e55760405161158f81610f62565b604081527f4142434445464748494a4b4c4d4e4f505152535455565758595a61626364656660208201527f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f604082015281516002928382018092116116cf5760039182900480851b94906001600160fe1b038116036116cf579261162b61161586610f9e565b956116236040519788610f7d565b808752610f9e565b6020860190601f190136823793829183518401925b83811061167e575050505051068060011461166b57600214611660575090565b603d90600019015390565b50603d9081600019820153600119015390565b85600491979293949701918251600190603f9082828260121c16880101518453828282600c1c16880101518385015382828260061c1688010151888501531685010151878201530195929190611640565b634e487b7160e01b600052601160045260246000fd5b506040516116f281610f47565b6000815290565b80511561177e5761170a9051611a84565b611028604e60405180937f7b2274726169745f74797065223a202253746f636b20506f736974696f6e732260208301526b1610113b30b63ab2911d101160a11b6040830152611763815180926020604c86019101610e83565b810161227d60f01b604c82015203602e810184520182610f7d565b5060405161178b81610f62565b602f81527f7b2274726169745f74797065223a202253746f636b20506f736974696f6e732260208201526e2c202276616c7565223a202230227d60881b604082015290565b805115611857576117e19051611a84565b611028605060405180937f7b2274726169745f74797065223a202257617272616e7420506f736974696f6e60208301526d39911610113b30b63ab2911d101160911b604083015261183c815180926020604e86019101610e83565b810161227d60f01b604e820152036030810184520182610f7d565b5060405161186481610f62565b603181527f7b2274726169745f74797065223a202257617272616e7420506f736974696f6e60208201527073222c202276616c7565223a202230227d60781b604082015290565b805115611936576118bc9051611a84565b611028605460405180937f7b2274726169745f74797065223a2022436f6e7665727469626c6520506f73696020830152713a34b7b739911610113b30b63ab2911d101160711b604083015261191b815180926020605286019101610e83565b810161227d60f01b6052820152036034810184520182610f7d565b5060405161194381610f62565b603581527f7b2274726169745f74797065223a2022436f6e7665727469626c6520506f736960208201527474696f6e73222c202276616c7565223a202230227d60581b604082015290565b805115611a245761199f9051611a84565b611028605c60405180937f7b2274726169745f74797065223a202245717569747920436f6d70656e73617460208301527f696f6e20506f736974696f6e73222c202276616c7565223a20220000000000006040830152611a09815180926020605a86019101610e83565b810161227d60f01b605a82015203603c810184520182610f7d565b50604051611a3181610f62565b603d81527f7b2274726169745f74797065223a202245717569747920436f6d70656e73617460208201527f696f6e20506f736974696f6e73222c202276616c7565223a202230227d000000604082015290565b8015611b475780816000925b611b2d5750611a9e82610f9e565b91611aac6040519384610f7d565b808352601f19611abb82610f9e565b01908260209236848701375b611ad15750505090565b60001981019081116116cf578092600a91603083830681018091116116cf578651821015611b175760f81b6001600160f81b03191660001a908601840153049182611ac7565b634e487b7160e01b600052603260045260246000fd5b909160001981146116cf576001019190600a900480611a90565b50604051604081018181106001600160401b038211176105fa5760405260018152600360fc1b60208201529056", - "sourceMap": "522:4539:72:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;522:4539:72;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;;;;;4508:18:40;522:4539:72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;522:4539:72;;;;;;-1:-1:-1;522:4539:72;;;6794:7:40;522:4539:72;;;;;;-1:-1:-1;;;;;522:4539:72;7208:31:40;1377:59:72;;522:4539;;-1:-1:-1;;;1546:70:72;;522:4539;;;;;-1:-1:-1;;;;;;522:4539:72;1546:70;;;522:4539;-1:-1:-1;522:4539:72;;;1571:4;1546:70;;;;;;;522:4539;1546:70;;;522:4539;;1761:552;522:4539;1835:430;;522:4539;1952:17;522:4539;;1952:17;:::i;:::-;2646:16;2623:40;2646:16;;2623:40;:::i;:::-;2735:18;2560:441;;2710:44;522:4539;2735:18;;;2710:44;:::i;:::-;2830:22;2900:58;2928:29;2801:52;522:4539;2830:22;;;2801:52;:::i;:::-;2928:29;;;2900:58;:::i;:::-;522:4539;;2560:441;;;522:4539;2560:441;;522:4539;-1:-1:-1;;;522:4539:72;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;522:4539:72;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;-1:-1:-1;;;522:4539:72;;;;2560:441;;;;;;;;;:::i;:::-;522:4539;;1835:430;;;522:4539;;1835:430;;522:4539;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;-1:-1:-1;;;522:4539:72;;;;-1:-1:-1;;;522:4539:72;;;;;;;;;;;;;;:::i;:::-;;-1:-1:-1;;;522:4539:72;;;;1835:430;;;;;;;;;:::i;:::-;1761:552;:::i;:::-;1670:661;522:4539;;;1670:661;;522:4539;;1670:661;;522:4539;;;;;;;;;;;;;:::i;:::-;;;1670:661;;;;;;;;;:::i;:::-;522:4539;;;;;;;;;;;;;:::i;:::-;;;;1546:70;;;-1:-1:-1;1546:70:72;522:4539;1546:70;;;;;;:::i;:::-;522:4539;1546:70;;;;522:4539;;;;;;-1:-1:-1;;;;;522:4539:72;;;;;;1546:70;522:4539;1546:70;;;;522:4539;;;;;;;;;;:::i;:::-;;;-1:-1:-1;;;;;522:4539:72;;;;;;1546:70;;;522:4539;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;1546:70;;;;522:4539;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;522:4539:72;;;;;;1546:70;;;522:4539;;;;;;;;1546:70;;;;522:4539;;;;;;;:::i;:::-;;;;;;;;;-1:-1:-1;;;;;522:4539:72;;;;;;1546:70;;;522:4539;;;;;;;;1546:70;;;;522:4539;;;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;;;;;522:4539:72;;;;;1546:70;;;;522:4539;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;1546:70;;;;522:4539;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;522:4539:72;;;;;;;-1:-1:-1;1761:552:72;1546:70;;522:4539;;1546:70;;;;522:4539;;;;;;;;;;;;-1:-1:-1;;;;;522:4539:72;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1546:70;;;;522:4539;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1546:70;522:4539;;;;;;;;;1377:59;522:4539;;-1:-1:-1;;;1407:29:72;;522:4539;;1407:29;522:4539;;;;;;-1:-1:-1;;522:4539:72;;;;;;:::i;:::-;;;:::i;:::-;;;;-1:-1:-1;;;;;522:4539:72;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;-1:-1:-1;;522:4539:72;;;;;;:::i;:::-;;;;;;;;;;;;-1:-1:-1;;;;;522:4539:72;;719:10:46;12990:17:40;;522:4539:72;;719:10:46;522:4539:72;;13047:18:40;522:4539:72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;13108:41:40;522:4539:72;719:10:46;13108:41:40;;522:4539:72;;;;-1:-1:-1;;;522:4539:72;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;522:4539:72;;;;;;;2721:7:40;522:4539:72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;-1:-1:-1;522:4539:72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;522:4539:72;;;;;;;;;;;-1:-1:-1;;522:4539:72;;;;;;;;;;;;;;;-1:-1:-1;522:4539:72;;-1:-1:-1;522:4539:72;;-1:-1:-1;522:4539:72;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;522:4539:72;;;;-1:-1:-1;;;;;522:4539:72;;:::i;:::-;;2028:19:40;;522:4539:72;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;522:4539:72;;;;;;;;;;;;;;;;;-1:-1:-1;;;522:4539:72;;;;;;;;;;;;;-1:-1:-1;;522:4539:72;;;;;;;;;:::i;:::-;;;-1:-1:-1;;;;;522:4539:72;;;;;;;;;;5077:39:40;522:4539:72;;;:::i;:::-;;;;;;;;:::i;:::-;;;;5077:39:40;:::i;522:4539:72:-;;;;4893:7:40;522:4539:72;;;:::i;:::-;719:10:46;4763:99:40;4771:41;719:10:46;;4771:41:40;:::i;:::-;4763:99;:::i;:::-;4893:7;:::i;522:4539:72:-;;;;;;-1:-1:-1;;522:4539:72;;;;945:10;522:4539;;;919:25;522:4539;;;;;;;;;;;;;;;971:19;522:4539;;;;;;971:39;967:93;;-1:-1:-1;522:4539:72;;;6794:7:40;522:4539:72;;;;;;-1:-1:-1;;;;;522:4539:72;1169:69;;945:10;9004:16:40;522:4539:72;;9710:7:40;9076:16;9067:58;9076:16;;-1:-1:-1;522:4539:72;6794:7:40;522:4539:72;;;;;;;;-1:-1:-1;522:4539:72;;;7208:31:40;;7120:126;;9076:16;9075:17;9067:58;:::i;:::-;-1:-1:-1;522:4539:72;;;6794:7:40;522:4539:72;;;;;;9271:58:40;;-1:-1:-1;;;;;522:4539:72;7208:31:40;;9280:16;7120:126;9271:58;945:10:72;522:4539;;;;;;;;9688:1:40;522:4539:72;;;;;;;;;;;;945:10;522:4539;;;;;;;;;945:10;522:4539;9747:33:40;;;;522:4539:72;;;;;;;;;;;;;;;;;;;;;;;;;;;1169:69;522:4539;;-1:-1:-1;;;1212:15:72;;522:4539;;1212:15;967:93;522:4539;;-1:-1:-1;;;1033:16:72;;522:4539;;1033:16;522:4539;;;;;;-1:-1:-1;;522:4539:72;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;522:4539:72;;3564:23:40;522:4539:72;3564:23:40;:::i;:::-;522:4539:72;;;3605:11:40;;;;522:4539:72;;719:10:46;;3686:21:40;:62;;;;;522:4539:72;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;522:4539:72;;;;;12699:23:40;522:4539:72;12699:23:40;:::i;:::-;522:4539:72;12690:46:40;522:4539:72;12690:46:40;;522:4539:72;;;;-1:-1:-1;;;522:4539:72;;;;;;;;;;;;;;;;;;;;;;;;;3686:62:40;522:4539:72;;;;4508:18:40;522:4539:72;;;;;719:10:46;522:4539:72;;;;;;;;;;3686:62:40;;;522:4539:72;;;-1:-1:-1;;;522:4539:72;;;;;;;;;;;;;;;;;-1:-1:-1;;;522:4539:72;;;;;;;;;;;;;-1:-1:-1;;522:4539:72;;;;;;;;;:::i;:::-;;;;;;-1:-1:-1;;522:4539:72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;-1:-1:-1;522:4539:72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;522:4539:72;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;522:4539:72;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;1707:40:40;;;:104;;;;522:4539:72;1707:156:40;;;;522:4539:72;;;;;;;1707:156:40;-1:-1:-1;;;937:40:49;;-1:-1:-1;1707:156:40;;;:104;-1:-1:-1;;;1763:48:40;;;-1:-1:-1;1707:104:40;;522:4539:72;;;;;;;;-1:-1:-1;;522:4539:72;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;-1:-1:-1;;522:4539:72;;;;:::o;:::-;;;;-1:-1:-1;;;;;522:4539:72;;;;;;:::o;:::-;;;;-1:-1:-1;;;;;522:4539:72;;;;;;:::o;:::-;;;;;;;;;-1:-1:-1;;;;;522:4539:72;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;-1:-1:-1;;;;;522:4539:72;;;;;;;:::o;:::-;;;;;;;-1:-1:-1;;;;;522:4539:72;;;;;;;:::o;:::-;;;;;;;-1:-1:-1;;;;;522:4539:72;;;;;;;:::o;:::-;;;;;;;;;;;;;-1:-1:-1;;;;;522:4539:72;;;;;;;:::o;:::-;-1:-1:-1;;;;;522:4539:72;;;;;;-1:-1:-1;;522:4539:72;;;;:::o;:::-;;;;:::o;:::-;;;-1:-1:-1;;;522:4539:72;;;;;;;;;;;;;;;;;;;;2190:219:40;-1:-1:-1;522:4539:72;;;6794:7:40;522:4539:72;;;;;;-1:-1:-1;;;;;522:4539:72;2324:56:40;2332:19;;;2324:56;:::i;:::-;2190:219;:::o;3935:167::-;-1:-1:-1;522:4539:72;;;6794:7:40;522:4539:72;;;;;;13313:53:40;;-1:-1:-1;;;;;522:4539:72;7208:31:40;;13313:53;:::i;:::-;-1:-1:-1;522:4539:72;;;4071:15:40;522:4539:72;;;;;;-1:-1:-1;;;;;522:4539:72;;3935:167:40:o;522:4539:72:-;;;;:::o;:::-;;;-1:-1:-1;;;522:4539:72;;;;;;;;;;;;;;;;;-1:-1:-1;;;522:4539:72;;;;;;;5189:276:40;;6484:47;5189:276;;;5311:99;5319:41;719:10:46;;5319:41:40;:::i;5311:99::-;6458:7;;;;;:::i;:::-;6484:47;:::i;:::-;522:4539:72;;;5189:276:40:o;522:4539:72:-;;;-1:-1:-1;;;522:4539:72;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;522:4539:72;;;;;;:::o;7404:261:40:-;;-1:-1:-1;;;;;522:4539:72;;7529:23:40;7404:261;7529:23;:::i;:::-;522:4539:72;;;7570:16:40;;;;:52;;;;;7404:261;7570:87;;;;;7404:261;7562:96;;;7404:261;:::o;7570:87::-;7626:20;;;;;;:::i;:::-;522:4539:72;7626:31:40;7570:87;;;;;:52;522:4539:72;;;-1:-1:-1;522:4539:72;4508:18:40;522:4539:72;;;-1:-1:-1;522:4539:72;;-1:-1:-1;522:4539:72;;;;;-1:-1:-1;522:4539:72;;;7570:52:40;;;;522:4539:72;;;;:::o;:::-;;;-1:-1:-1;;;522:4539:72;;;;;;;;;;;;;;;;;-1:-1:-1;;;522:4539:72;;;;;;;11257:1203:40;;11346:81;11257:1203;11354:23;;;:::i;:::-;-1:-1:-1;;;;;522:4539:72;;;;;;;;;;;11354:31:40;;11346:81;:::i;:::-;522:4539:72;11445:16:40;;;522:4539:72;;11655:23:40;11647:81;11655:23;;;;:::i;:::-;522:4539:72;11655:31:40;11647:81;:::i;:::-;12374:27;11459:1;522:4539:72;;;11797:15:40;522:4539:72;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;12337:7:40;522:4539:72;;;;;;;;;;;;;12374:27:40;;11257:1203::o;522:4539:72:-;;;-1:-1:-1;;;522:4539:72;;;;;;;;;;;;;;;;;-1:-1:-1;;;522:4539:72;;;;;;;13925:831:40;;;;;1702:19:44;;:23;:19;;522:4539:72;;;;;;;;;14129:71:40;;;;719:10:46;14129:71:40;;;522:4539:72;;;;;;;;;;;;;;;;;;;;;;14129:71:40;;;522:4539:72;;;;;;;;:::i;:::-;14129:71:40;-1:-1:-1;;522:4539:72;;;;14129:71:40;;;;;;;14090:660;-1:-1:-1;14125:573:40;;14317:381;;;522:4539:72;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;14367:18:40;;;522:4539:72;;-1:-1:-1;;;14409:60:40;;522:4539:72;14409:60:40;14129:71;14409:60;;;:::i;14363:321::-;14571:95;;522:4539:72;;;;;14125:573:40;-1:-1:-1;;;;;;522:4539:72;14250:51:40;;-1:-1:-1;522:4539:72;-1:-1:-1;14243:58:40:o;14129:71::-;;;;;;;;;;;;;;;;;:::i;:::-;;;522:4539:72;;;;;-1:-1:-1;;;;;;522:4539:72;;;;;;14129:71:40;;;;522:4539:72;;;;14129:71:40;;;;;14090:660;14728:11;;;;;14735:4;14728:11;:::o;522:4539:72:-;;;;:::o;:::-;;;-1:-1:-1;;;522:4539:72;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;522:4539:72;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;;;;522:4539:72;;;;;;:::o;:::-;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;522:4539:72;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;522:4539:72;;;;;;;;;505:3026:45;522:4539:72;;795:16:45;791:31;;522:4539:72;;;;;:::i;:::-;;;;;;;;;;;;;;;;1357:1:45;345:66;;;;;;;;;1362:1;345:66;;;;;;;;;-1:-1:-1;;;;;345:66:45;;;;;522:4539:72;345:66:45;522:4539:72;;;:::i;:::-;;;;;;;;:::i;:::-;;;;345:66:45;:::i;:::-;522:4539:72;345:66:45;;;-1:-1:-1;;345:66:45;;;;1419:2082;;;;;;;;;;;;;;;;;;;;1362:1;1419:2082;1362:1;;;1419:2082;;;;3511:13;505:3026;:::o;1419:2082::-;;;-1:-1:-1;;1419:2082:45;;505:3026;:::o;1419:2082::-;-1:-1:-1;1419:2082:45;;;-1:-1:-1;;1419:2082:45;;;-1:-1:-1;;1419:2082:45;;505:3026;:::o;1419:2082::-;;1337:1;1419:2082;;;;;;;;;;522:4539:72;1419:2082:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;345:66;522:4539:72;;;810:1:45;345:66;;1337:1;345:66;;810:1;345:66;791:31;522:4539:72;;;;;;:::i;:::-;810:1:45;522:4539:72;;813:9:45;:::o;3072:334:72:-;522:4539;;3196:21;3192:83;;3365:26;522:4539;;3365:26;:::i;:::-;3300:98;;522:4539;;3300:98;;522:4539;3300:98;;;522:4539;;;;;;;;;;;;;3300:98;522:4539;;;;;;:::i;:::-;;;;;;;;;;3300:98;;;;;;;;;:::i;3192:83::-;522:4539;;;;;;:::i;:::-;;;;;;;;;-1:-1:-1;;;522:4539:72;;;;;3219:56::o;3412:342::-;522:4539;;3540:21;3536:85;;3713:26;522:4539;;3713:26;:::i;:::-;3646:100;;522:4539;;3646:100;;522:4539;3646:100;;;522:4539;;;;;;;;;;;;;3646:100;522:4539;;;;;;:::i;:::-;;;;;;;;;;3646:100;;;;;;;;;:::i;3536:85::-;522:4539;;;;;;:::i;:::-;;;;;;;;;-1:-1:-1;;;522:4539:72;;;;;3563:58::o;3760:358::-;522:4539;;3896:21;3892:89;;4077:26;522:4539;;4077:26;:::i;:::-;4006:104;;522:4539;;4006:104;;522:4539;4006:104;;;522:4539;;;;;;;;;;;;;4006:104;522:4539;;;;;;:::i;:::-;;;;;;;;;;4006:104;;;;;;;;;:::i;3892:89::-;522:4539;;;;;;:::i;:::-;;;;;;;;;-1:-1:-1;;;522:4539:72;;;;;3919:62::o;4124:380::-;522:4539;;4266:21;4262:97;;4463:26;522:4539;;4463:26;:::i;:::-;4384:112;;522:4539;;4384:112;;522:4539;4384:112;;;522:4539;;;;;;;;;;;4384:112;522:4539;;;;;;:::i;:::-;;;;;;;;;;4384:112;;;;;;;;;:::i;4262:97::-;522:4539;;;;;;:::i;:::-;;;;;;;;;;;;;;4289:70;:::o;4510:549::-;4629:10;;4625:51;;4685:20;4715:14;4638:1;4739:75;4746:9;;;522:4539;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;-1:-1:-1;;522:4539:72;;;:::i;:::-;;;;;345:66:45;;522:4539:72;;;345:66:45;4879:10:72;;;5031:21;;;4510:549;:::o;4872:150::-;-1:-1:-1;;522:4539:72;;;;;;;4905:11;4801:2;;4960;;522:4539;;;345:66:45;;;;;;;522:4539:72;;;;;;;;;-1:-1:-1;;;;;;522:4539:72;4638:1;4930:56;;522:4539;;;;4930:56;345:66:45;;;4872:150:72;;522:4539;;;;4638:1;522:4539;;;;;4638:1;522:4539;4739:75;522:4539;;-1:-1:-1;;522:4539:72;;;;;;;4739:75;4801:2;345:66:45;;;4739:75:72;;4625:51;522:4539;;;;;;;;;-1:-1:-1;;;;;522:4539:72;;;;;;;;;;-1:-1:-1;;;522:4539:72;;;;4655:10;:::o", - "linkReferences": {} - }, - "methodIdentifiers": { - "approve(address,uint256)": "095ea7b3", - "balanceOf(address)": "70a08231", - "getApproved(uint256)": "081812fc", - "isApprovedForAll(address,address)": "e985e9c5", - "mint()": "1249c58b", - "name()": "06fdde03", - "ownerOf(uint256)": "6352211e", - "safeTransferFrom(address,address,uint256)": "42842e0e", - "safeTransferFrom(address,address,uint256,bytes)": "b88d4fde", - "setApprovalForAll(address,bool)": "a22cb465", - "supportsInterface(bytes4)": "01ffc9a7", - "symbol()": "95d89b41", - "tokenURI(uint256)": "c87b56dd", - "transferFrom(address,address,uint256)": "23b872dd" - }, - "rawMetadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AlreadyMinted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotStakeholder\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"URIQueryForNonexistentToken\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"approved\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"ApprovalForAll\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"getApproved\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"isApprovedForAll\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ownerOf\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"setApprovalForAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"tokenURI\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"events\":{\"Approval(address,address,uint256)\":{\"details\":\"Emitted when `owner` enables `approved` to manage the `tokenId` token.\"},\"ApprovalForAll(address,address,bool)\":{\"details\":\"Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\"},\"Transfer(address,address,uint256)\":{\"details\":\"Emitted when `tokenId` token is transferred from `from` to `to`.\"}},\"kind\":\"dev\",\"methods\":{\"approve(address,uint256)\":{\"details\":\"See {IERC721-approve}.\"},\"balanceOf(address)\":{\"details\":\"See {IERC721-balanceOf}.\"},\"getApproved(uint256)\":{\"details\":\"See {IERC721-getApproved}.\"},\"isApprovedForAll(address,address)\":{\"details\":\"See {IERC721-isApprovedForAll}.\"},\"name()\":{\"details\":\"See {IERC721Metadata-name}.\"},\"ownerOf(uint256)\":{\"details\":\"See {IERC721-ownerOf}.\"},\"safeTransferFrom(address,address,uint256)\":{\"details\":\"See {IERC721-safeTransferFrom}.\"},\"safeTransferFrom(address,address,uint256,bytes)\":{\"details\":\"See {IERC721-safeTransferFrom}.\"},\"setApprovalForAll(address,bool)\":{\"details\":\"See {IERC721-setApprovalForAll}.\"},\"supportsInterface(bytes4)\":{\"details\":\"See {IERC165-supportsInterface}.\"},\"symbol()\":{\"details\":\"See {IERC721Metadata-symbol}.\"},\"tokenURI(uint256)\":{\"details\":\"See {IERC721Metadata-tokenURI}.\"},\"transferFrom(address,address,uint256)\":{\"details\":\"See {IERC721-transferFrom}.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/lib/diamond/facets/StakeholderNFTFacet.sol\":\"StakeholderNFTFacet\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"appendCBOR\":false,\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@diamond/=src/lib/diamond/\",\":diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/\",\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\",\":openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/\"],\"viaIR\":true},\"sources\":{\"lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol\":{\"keccak256\":\"0x2c309e7df9e05e6ce15bedfe74f3c61b467fc37e0fae9eab496acf5ea0bbd7ff\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7063b5c98711a98018ba4635ac74cee1c1cfa2ea01099498e062699ed9530005\",\"dweb:/ipfs/QmeJ8rGXkcv7RrqLdAW8PCXPAykxVsddfYY6g5NaTwmRFE\"]},\"lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol\":{\"keccak256\":\"0x5bce51e11f7d194b79ea59fe00c9e8de9fa2c5530124960f29a24d4c740a3266\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7e66dfde185df46104c11bc89d08fa0760737aa59a2b8546a656473d810a8ea4\",\"dweb:/ipfs/QmXvyqtXPaPss2PD7eqPoSao5Szm2n6UMoiG8TZZDjmChR\"]},\"lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol\":{\"keccak256\":\"0xa82b58eca1ee256be466e536706850163d2ec7821945abd6b4778cfb3bee37da\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6e75cf83beb757b8855791088546b8337e9d4684e169400c20d44a515353b708\",\"dweb:/ipfs/QmYvPafLfoquiDMEj7CKHtvbgHu7TJNPSVPSCjrtjV8HjV\"]},\"lib/openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Metadata.sol\":{\"keccak256\":\"0x75b829ff2f26c14355d1cba20e16fe7b29ca58eb5fef665ede48bc0f9c6c74b9\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://a0a107160525724f9e1bbbab031defc2f298296dd9e331f16a6f7130cec32146\",\"dweb:/ipfs/QmemujxSd7gX8A9M8UwmNbz4Ms3U9FG9QfudUgxwvTmPWf\"]},\"lib/openzeppelin-contracts/contracts/utils/Address.sol\":{\"keccak256\":\"0x006dd67219697fe68d7fbfdea512e7c4cb64a43565ed86171d67e844982da6fa\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://2455248c8ddd9cc6a7af76a13973cddf222072427e7b0e2a7d1aff345145e931\",\"dweb:/ipfs/QmfYjnjRbWqYpuxurqveE6HtzsY1Xx323J428AKQgtBJZm\"]},\"lib/openzeppelin-contracts/contracts/utils/Base64.sol\":{\"keccak256\":\"0x5f3461639fe20794cfb4db4a6d8477388a15b2e70a018043084b7c4bedfa8136\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://77e5309e2cc4cdc3395214edb0ff43ff5a5f7373f5a425383e540f6fab530f96\",\"dweb:/ipfs/QmTV8DZ9knJDa3b5NPBFQqjvTzodyZVjRUg5mx5A99JPLJ\"]},\"lib/openzeppelin-contracts/contracts/utils/Context.sol\":{\"keccak256\":\"0xe2e337e6dde9ef6b680e07338c493ebea1b5fd09b43424112868e9cc1706bca7\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6df0ddf21ce9f58271bdfaa85cde98b200ef242a05a3f85c2bc10a8294800a92\",\"dweb:/ipfs/QmRK2Y5Yc6BK7tGKkgsgn3aJEQGi5aakeSPZvS65PV8Xp3\"]},\"lib/openzeppelin-contracts/contracts/utils/Strings.sol\":{\"keccak256\":\"0x3088eb2868e8d13d89d16670b5f8612c4ab9ff8956272837d8e90106c59c14a0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://b81d9ff6559ea5c47fc573e17ece6d9ba5d6839e213e6ebc3b4c5c8fe4199d7f\",\"dweb:/ipfs/QmPCW1bFisUzJkyjroY3yipwfism9RRCigCcK1hbXtVM8n\"]},\"lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol\":{\"keccak256\":\"0xd10975de010d89fd1c78dc5e8a9a7e7f496198085c151648f20cba166b32582b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://fb0048dee081f6fffa5f74afc3fb328483c2a30504e94a0ddd2a5114d731ec4d\",\"dweb:/ipfs/QmZptt1nmYoA5SgjwnSgWqgUSDgm4q52Yos3xhnMv3MV43\"]},\"lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol\":{\"keccak256\":\"0x447a5f3ddc18419d41ff92b3773fb86471b1db25773e07f877f548918a185bf1\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://be161e54f24e5c6fae81a12db1a8ae87bc5ae1b0ddc805d82a1440a68455088f\",\"dweb:/ipfs/QmP7C3CHdY9urF4dEMb9wmsp1wMxHF6nhA2yQE5SKiPAdy\"]},\"lib/openzeppelin-contracts/contracts/utils/math/Math.sol\":{\"keccak256\":\"0xe4455ac1eb7fc497bb7402579e7b4d64d928b846fce7d2b6fde06d366f21c2b3\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://cc8841b3cd48ad125e2f46323c8bad3aa0e88e399ec62acb9e57efa7e7c8058c\",\"dweb:/ipfs/QmSqE4mXHA2BXW58deDbXE8MTcsL5JSKNDbm23sVQxRLPS\"]},\"lib/openzeppelin-contracts/contracts/utils/math/SignedMath.sol\":{\"keccak256\":\"0xf92515413956f529d95977adc9b0567d583c6203fc31ab1c23824c35187e3ddc\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://c50fcc459e49a9858b6d8ad5f911295cb7c9ab57567845a250bf0153f84a95c7\",\"dweb:/ipfs/QmcEW85JRzvDkQggxiBBLVAasXWdkhEysqypj9EaB6H2g6\"]},\"src/lib/diamond/Storage.sol\":{\"keccak256\":\"0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d\",\"dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL\"]},\"src/lib/diamond/Structs.sol\":{\"keccak256\":\"0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab\",\"dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD\"]},\"src/lib/diamond/facets/StakeholderFacet.sol\":{\"keccak256\":\"0x1d636a399b0bcbe8fa4115b6bd13b09cbc4dfaf73ce862a98419100ddeee56e4\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5748c73c6a7422af0b198ea6c150d21ce5a77413292f55c68543f4a8da8255b9\",\"dweb:/ipfs/Qma642YoBMLAdmxPKhks2ki8j6UmCQiH2vyQirJaHXz5eZ\"]},\"src/lib/diamond/facets/StakeholderNFTFacet.sol\":{\"keccak256\":\"0x4f73f0bf6b9c2ed639ae81e84f133283dd08e2c97dae5aa2d73e4d69fbee28c6\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://c7226634490391eb3b4919234292d9a62de8fc0d5a8ae53da0541ed594d4347d\",\"dweb:/ipfs/QmRoUEwX3V868EyiYqaW9ekHgH9kAH2Vpkgmu8SSpatHMR\"]},\"src/lib/diamond/libraries/ValidationLib.sol\":{\"keccak256\":\"0xfcd772b0670acded810468d772729715e3cd4db25c02a0da77c7445903e1cc5f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d572672df16392825ed86c32a53453247f62b481787db0498993f857623e1ee6\",\"dweb:/ipfs/Qmbpoa5EZ7yRcB1mZkmb5nhfxhhFY9g8tMJxPf9ZrzfNfL\"]}},\"version\":1}", - "metadata": { - "compiler": { - "version": "0.8.20+commit.a1b79de6" - }, - "language": "Solidity", - "output": { - "abi": [ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [], - "type": "error", - "name": "AlreadyMinted" - }, - { - "inputs": [], - "type": "error", - "name": "NotStakeholder" - }, - { - "inputs": [], - "type": "error", - "name": "URIQueryForNonexistentToken" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address", - "indexed": true - }, - { - "internalType": "address", - "name": "approved", - "type": "address", - "indexed": true - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256", - "indexed": true - } - ], - "type": "event", - "name": "Approval", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address", - "indexed": true - }, - { - "internalType": "address", - "name": "operator", - "type": "address", - "indexed": true - }, - { - "internalType": "bool", - "name": "approved", - "type": "bool", - "indexed": false - } - ], - "type": "event", - "name": "ApprovalForAll", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "address", - "name": "from", - "type": "address", - "indexed": true - }, - { - "internalType": "address", - "name": "to", - "type": "address", - "indexed": true - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256", - "indexed": true - } - ], - "type": "event", - "name": "Transfer", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "approve" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function", - "name": "balanceOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ] - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function", - "name": "getApproved", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ] - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "operator", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function", - "name": "isApprovedForAll", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ] - }, - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "function", - "name": "mint" - }, - { - "inputs": [], - "stateMutability": "view", - "type": "function", - "name": "name", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ] - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function", - "name": "ownerOf", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ] - }, - { - "inputs": [ - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "safeTransferFrom" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "safeTransferFrom" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "operator", - "type": "address" - }, - { - "internalType": "bool", - "name": "approved", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "setApprovalForAll" - }, - { - "inputs": [ - { - "internalType": "bytes4", - "name": "interfaceId", - "type": "bytes4" - } - ], - "stateMutability": "view", - "type": "function", - "name": "supportsInterface", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ] - }, - { - "inputs": [], - "stateMutability": "view", - "type": "function", - "name": "symbol", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ] - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function", - "name": "tokenURI", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ] - }, - { - "inputs": [ - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "transferFrom" - } - ], - "devdoc": { - "kind": "dev", - "methods": { - "approve(address,uint256)": { - "details": "See {IERC721-approve}." - }, - "balanceOf(address)": { - "details": "See {IERC721-balanceOf}." - }, - "getApproved(uint256)": { - "details": "See {IERC721-getApproved}." - }, - "isApprovedForAll(address,address)": { - "details": "See {IERC721-isApprovedForAll}." - }, - "name()": { - "details": "See {IERC721Metadata-name}." - }, - "ownerOf(uint256)": { - "details": "See {IERC721-ownerOf}." - }, - "safeTransferFrom(address,address,uint256)": { - "details": "See {IERC721-safeTransferFrom}." - }, - "safeTransferFrom(address,address,uint256,bytes)": { - "details": "See {IERC721-safeTransferFrom}." - }, - "setApprovalForAll(address,bool)": { - "details": "See {IERC721-setApprovalForAll}." - }, - "supportsInterface(bytes4)": { - "details": "See {IERC165-supportsInterface}." - }, - "symbol()": { - "details": "See {IERC721Metadata-symbol}." - }, - "tokenURI(uint256)": { - "details": "See {IERC721Metadata-tokenURI}." - }, - "transferFrom(address,address,uint256)": { - "details": "See {IERC721-transferFrom}." - } - }, - "version": 1 - }, - "userdoc": { - "kind": "user", - "methods": {}, - "version": 1 - } - }, - "settings": { - "remappings": [ - "@diamond/=src/lib/diamond/", - "diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/", - "ds-test/=lib/forge-std/lib/ds-test/src/", - "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", - "forge-std/=lib/forge-std/src/", - "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", - "openzeppelin-contracts/=lib/openzeppelin-contracts/", - "openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/" - ], - "optimizer": { - "enabled": true, - "runs": 200 - }, - "metadata": { - "bytecodeHash": "none", - "appendCBOR": false - }, - "compilationTarget": { - "src/lib/diamond/facets/StakeholderNFTFacet.sol": "StakeholderNFTFacet" - }, - "evmVersion": "paris", - "libraries": {}, - "viaIR": true - }, - "sources": { - "lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol": { - "keccak256": "0x2c309e7df9e05e6ce15bedfe74f3c61b467fc37e0fae9eab496acf5ea0bbd7ff", - "urls": [ - "bzz-raw://7063b5c98711a98018ba4635ac74cee1c1cfa2ea01099498e062699ed9530005", - "dweb:/ipfs/QmeJ8rGXkcv7RrqLdAW8PCXPAykxVsddfYY6g5NaTwmRFE" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol": { - "keccak256": "0x5bce51e11f7d194b79ea59fe00c9e8de9fa2c5530124960f29a24d4c740a3266", - "urls": [ - "bzz-raw://7e66dfde185df46104c11bc89d08fa0760737aa59a2b8546a656473d810a8ea4", - "dweb:/ipfs/QmXvyqtXPaPss2PD7eqPoSao5Szm2n6UMoiG8TZZDjmChR" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol": { - "keccak256": "0xa82b58eca1ee256be466e536706850163d2ec7821945abd6b4778cfb3bee37da", - "urls": [ - "bzz-raw://6e75cf83beb757b8855791088546b8337e9d4684e169400c20d44a515353b708", - "dweb:/ipfs/QmYvPafLfoquiDMEj7CKHtvbgHu7TJNPSVPSCjrtjV8HjV" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Metadata.sol": { - "keccak256": "0x75b829ff2f26c14355d1cba20e16fe7b29ca58eb5fef665ede48bc0f9c6c74b9", - "urls": [ - "bzz-raw://a0a107160525724f9e1bbbab031defc2f298296dd9e331f16a6f7130cec32146", - "dweb:/ipfs/QmemujxSd7gX8A9M8UwmNbz4Ms3U9FG9QfudUgxwvTmPWf" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/utils/Address.sol": { - "keccak256": "0x006dd67219697fe68d7fbfdea512e7c4cb64a43565ed86171d67e844982da6fa", - "urls": [ - "bzz-raw://2455248c8ddd9cc6a7af76a13973cddf222072427e7b0e2a7d1aff345145e931", - "dweb:/ipfs/QmfYjnjRbWqYpuxurqveE6HtzsY1Xx323J428AKQgtBJZm" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/utils/Base64.sol": { - "keccak256": "0x5f3461639fe20794cfb4db4a6d8477388a15b2e70a018043084b7c4bedfa8136", - "urls": [ - "bzz-raw://77e5309e2cc4cdc3395214edb0ff43ff5a5f7373f5a425383e540f6fab530f96", - "dweb:/ipfs/QmTV8DZ9knJDa3b5NPBFQqjvTzodyZVjRUg5mx5A99JPLJ" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/utils/Context.sol": { - "keccak256": "0xe2e337e6dde9ef6b680e07338c493ebea1b5fd09b43424112868e9cc1706bca7", - "urls": [ - "bzz-raw://6df0ddf21ce9f58271bdfaa85cde98b200ef242a05a3f85c2bc10a8294800a92", - "dweb:/ipfs/QmRK2Y5Yc6BK7tGKkgsgn3aJEQGi5aakeSPZvS65PV8Xp3" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/utils/Strings.sol": { - "keccak256": "0x3088eb2868e8d13d89d16670b5f8612c4ab9ff8956272837d8e90106c59c14a0", - "urls": [ - "bzz-raw://b81d9ff6559ea5c47fc573e17ece6d9ba5d6839e213e6ebc3b4c5c8fe4199d7f", - "dweb:/ipfs/QmPCW1bFisUzJkyjroY3yipwfism9RRCigCcK1hbXtVM8n" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol": { - "keccak256": "0xd10975de010d89fd1c78dc5e8a9a7e7f496198085c151648f20cba166b32582b", - "urls": [ - "bzz-raw://fb0048dee081f6fffa5f74afc3fb328483c2a30504e94a0ddd2a5114d731ec4d", - "dweb:/ipfs/QmZptt1nmYoA5SgjwnSgWqgUSDgm4q52Yos3xhnMv3MV43" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol": { - "keccak256": "0x447a5f3ddc18419d41ff92b3773fb86471b1db25773e07f877f548918a185bf1", - "urls": [ - "bzz-raw://be161e54f24e5c6fae81a12db1a8ae87bc5ae1b0ddc805d82a1440a68455088f", - "dweb:/ipfs/QmP7C3CHdY9urF4dEMb9wmsp1wMxHF6nhA2yQE5SKiPAdy" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/utils/math/Math.sol": { - "keccak256": "0xe4455ac1eb7fc497bb7402579e7b4d64d928b846fce7d2b6fde06d366f21c2b3", - "urls": [ - "bzz-raw://cc8841b3cd48ad125e2f46323c8bad3aa0e88e399ec62acb9e57efa7e7c8058c", - "dweb:/ipfs/QmSqE4mXHA2BXW58deDbXE8MTcsL5JSKNDbm23sVQxRLPS" - ], - "license": "MIT" - }, - "lib/openzeppelin-contracts/contracts/utils/math/SignedMath.sol": { - "keccak256": "0xf92515413956f529d95977adc9b0567d583c6203fc31ab1c23824c35187e3ddc", - "urls": [ - "bzz-raw://c50fcc459e49a9858b6d8ad5f911295cb7c9ab57567845a250bf0153f84a95c7", - "dweb:/ipfs/QmcEW85JRzvDkQggxiBBLVAasXWdkhEysqypj9EaB6H2g6" - ], - "license": "MIT" - }, - "src/lib/diamond/Storage.sol": { - "keccak256": "0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f", - "urls": [ - "bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d", - "dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL" - ], - "license": "MIT" - }, - "src/lib/diamond/Structs.sol": { - "keccak256": "0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2", - "urls": [ - "bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab", - "dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/StakeholderFacet.sol": { - "keccak256": "0x1d636a399b0bcbe8fa4115b6bd13b09cbc4dfaf73ce862a98419100ddeee56e4", - "urls": [ - "bzz-raw://5748c73c6a7422af0b198ea6c150d21ce5a77413292f55c68543f4a8da8255b9", - "dweb:/ipfs/Qma642YoBMLAdmxPKhks2ki8j6UmCQiH2vyQirJaHXz5eZ" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/StakeholderNFTFacet.sol": { - "keccak256": "0x4f73f0bf6b9c2ed639ae81e84f133283dd08e2c97dae5aa2d73e4d69fbee28c6", - "urls": [ - "bzz-raw://c7226634490391eb3b4919234292d9a62de8fc0d5a8ae53da0541ed594d4347d", - "dweb:/ipfs/QmRoUEwX3V868EyiYqaW9ekHgH9kAH2Vpkgmu8SSpatHMR" - ], - "license": "MIT" - }, - "src/lib/diamond/libraries/ValidationLib.sol": { - "keccak256": "0xfcd772b0670acded810468d772729715e3cd4db25c02a0da77c7445903e1cc5f", - "urls": [ - "bzz-raw://d572672df16392825ed86c32a53453247f62b481787db0498993f857623e1ee6", - "dweb:/ipfs/Qmbpoa5EZ7yRcB1mZkmb5nhfxhhFY9g8tMJxPf9ZrzfNfL" - ], - "license": "MIT" - } - }, - "version": 1 - }, - "id": 72 -} \ No newline at end of file diff --git a/chain/out/StockClassFacet.sol/StockClassFacet.json b/chain/out/StockClassFacet.sol/StockClassFacet.json deleted file mode 100644 index ec1ebb40..00000000 --- a/chain/out/StockClassFacet.sol/StockClassFacet.json +++ /dev/null @@ -1,407 +0,0 @@ -{ - "abi": [ - { - "type": "function", - "name": "adjustAuthorizedShares", - "inputs": [ - { - "name": "stockClassId", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "newSharesAuthorized", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "createStockClass", - "inputs": [ - { - "name": "_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "_class_type", - "type": "string", - "internalType": "string" - }, - { - "name": "_price_per_share", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "_initial_share_authorized", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "event", - "name": "StockClassAuthorizedSharesAdjusted", - "inputs": [ - { - "name": "stockClassId", - "type": "bytes16", - "indexed": true, - "internalType": "bytes16" - }, - { - "name": "newSharesAuthorized", - "type": "uint256", - "indexed": false, - "internalType": "uint256" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "StockClassCreated", - "inputs": [ - { - "name": "id", - "type": "bytes16", - "indexed": true, - "internalType": "bytes16" - }, - { - "name": "classType", - "type": "string", - "indexed": true, - "internalType": "string" - }, - { - "name": "pricePerShare", - "type": "uint256", - "indexed": true, - "internalType": "uint256" - }, - { - "name": "initialSharesAuthorized", - "type": "uint256", - "indexed": false, - "internalType": "uint256" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "TxCreated", - "inputs": [ - { - "name": "txType", - "type": "uint8", - "indexed": false, - "internalType": "enum TxType" - }, - { - "name": "txData", - "type": "bytes", - "indexed": false, - "internalType": "bytes" - } - ], - "anonymous": false - }, - { - "type": "error", - "name": "InvalidSharesAuthorized", - "inputs": [] - }, - { - "type": "error", - "name": "StockClassAlreadyExists", - "inputs": [ - { - "name": "stock_class_id", - "type": "bytes16", - "internalType": "bytes16" - } - ] - }, - { - "type": "error", - "name": "StockClassNotFound", - "inputs": [ - { - "name": "stock_class_id", - "type": "bytes16", - "internalType": "bytes16" - } - ] - } - ], - "bytecode": { - "object": "0x60808060405234610016576105d7908161001c8239f35b600080fdfe6080604052600436101561001257600080fd5b60003560e01c80634afd58fa1461019c576367ede6281461003257600080fd5b346101975760403660031901126101975761004b610515565b602435906001600160801b031916806000527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf9602052604060002054801561017e5760001981019081116101685760046100a58492610535565b5001557faa2496f65161bfe6107d5ae2865fe60c5760e90627cdd8a472be95944d035f486020604051848152a2604051906020820190815260208252604082019082821067ffffffffffffffff831117610152577f88aa35e399ceb8d6b2e810abb3df607c140128e7c089c428f212598bda6a7eb79260609183604052600284526040838301526101438251809281608086015260a0850190610593565b601f01601f19168101030190a1005b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b604051634734e78560e01b815260048101839052602490fd5b600080fd5b34610197576080366003190112610197576101b5610515565b67ffffffffffffffff60243581811161019757366023820112156101975780600401359082821161015257601f19603f601f19601f850116011660800160808110848211176101525760405281608052366024838301011161019757600091602091819060240160a03760800101526001600160801b0319908183166000527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf96020526040600020546104fb5760405160a081018181108382111761015257604052828416815260208101916080835260443560408301526000606083015260643560808301526000805160206105b78339815191525468010000000000000000811015610152578060016102da92016000805160206105b783398151915255610535565b9390936104e557825160801c85855416178455518051918211610152576001840154600181811c911680156104db575b60208210146104c557601f811161047e575b50602090601f8311600114610409579180600494926080946000926103fe575b50508160011b916000199060031b1c19161760018501555b604081015160028501556060810151600385015501519101556000805160206105b7833981519152548183166000527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf9602052604060002055604051806080516103c381836020608001610593565b8101039020907fc7496d70298fcc793e1d058617af680232585e302f0185b14bba498b247a9c1d6020604051926064358452604435951692a4005b01519050888061033c565b906001850160005260206000209160005b601f19851681106104665750926004949260019260809583601f1981161061044d575b505050811b016001850155610354565b015160001960f88460031b161c1916905588808061043d565b9192602060018192868501518155019401920161041a565b600185016000526020600020601f840160051c8101602085106104be575b601f830160051c820181106104b257505061031c565b6000815560010161049c565b508061049c565b634e487b7160e01b600052602260045260246000fd5b90607f169061030a565b634e487b7160e01b600052600060045260246000fd5b604051631339dd8760e21b81528284166004820152602490fd5b600435906fffffffffffffffffffffffffffffffff198216820361019757565b6000805160206105b783398151915290815481101561057d57600591600052027fee6f7b31ced919e4b28e9b6e4406f8a625ca3bdeb0e9e0c4c4bc61059574b3180190600090565b634e487b7160e01b600052603260045260246000fd5b60005b8381106105a65750506000910152565b818101518382015260200161059656fe2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf8", - "sourceMap": "289:1824:40:-:0;;;;;;;;;;;;;;;;;", - "linkReferences": {} - }, - "deployedBytecode": { - "object": "0x6080604052600436101561001257600080fd5b60003560e01c80634afd58fa1461019c576367ede6281461003257600080fd5b346101975760403660031901126101975761004b610515565b602435906001600160801b031916806000527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf9602052604060002054801561017e5760001981019081116101685760046100a58492610535565b5001557faa2496f65161bfe6107d5ae2865fe60c5760e90627cdd8a472be95944d035f486020604051848152a2604051906020820190815260208252604082019082821067ffffffffffffffff831117610152577f88aa35e399ceb8d6b2e810abb3df607c140128e7c089c428f212598bda6a7eb79260609183604052600284526040838301526101438251809281608086015260a0850190610593565b601f01601f19168101030190a1005b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b604051634734e78560e01b815260048101839052602490fd5b600080fd5b34610197576080366003190112610197576101b5610515565b67ffffffffffffffff60243581811161019757366023820112156101975780600401359082821161015257601f19603f601f19601f850116011660800160808110848211176101525760405281608052366024838301011161019757600091602091819060240160a03760800101526001600160801b0319908183166000527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf96020526040600020546104fb5760405160a081018181108382111761015257604052828416815260208101916080835260443560408301526000606083015260643560808301526000805160206105b78339815191525468010000000000000000811015610152578060016102da92016000805160206105b783398151915255610535565b9390936104e557825160801c85855416178455518051918211610152576001840154600181811c911680156104db575b60208210146104c557601f811161047e575b50602090601f8311600114610409579180600494926080946000926103fe575b50508160011b916000199060031b1c19161760018501555b604081015160028501556060810151600385015501519101556000805160206105b7833981519152548183166000527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf9602052604060002055604051806080516103c381836020608001610593565b8101039020907fc7496d70298fcc793e1d058617af680232585e302f0185b14bba498b247a9c1d6020604051926064358452604435951692a4005b01519050888061033c565b906001850160005260206000209160005b601f19851681106104665750926004949260019260809583601f1981161061044d575b505050811b016001850155610354565b015160001960f88460031b161c1916905588808061043d565b9192602060018192868501518155019401920161041a565b600185016000526020600020601f840160051c8101602085106104be575b601f830160051c820181106104b257505061031c565b6000815560010161049c565b508061049c565b634e487b7160e01b600052602260045260246000fd5b90607f169061030a565b634e487b7160e01b600052600060045260246000fd5b604051631339dd8760e21b81528284166004820152602490fd5b600435906fffffffffffffffffffffffffffffffff198216820361019757565b6000805160206105b783398151915290815481101561057d57600591600052027fee6f7b31ced919e4b28e9b6e4406f8a625ca3bdeb0e9e0c4c4bc61059574b3180190600090565b634e487b7160e01b600052603260045260246000fd5b60005b8381106105a65750506000910152565b818101518382015260200161059656fe2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf8", - "sourceMap": "289:1824:40:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;289:1824:40;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;289:1824:40;;;;;1639:18;289:1824;;;;;;1686:20;;1682:90;;-1:-1:-1;;289:1824:40;;;;;;;;1814:36;;;;:::i;:::-;1860:28;;289:1824;1926:69;289:1824;;;;;;1926:69;289:1824;;2072:31;289:1824;2072:31;;289:1824;;;;2072:31;;289:1824;;;;;;;;;;;;;1098:25:32;289:1824:40;;;;;;2023:47;289:1824;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;-1:-1:-1;;289:1824:40;;;1098:25:32;;;;289:1824:40;;;;;;;;;;;;;;;;;;;;;;;;;1682:90;289:1824;;-1:-1:-1;;;1729:32:40;;289:1824;1729:32;;289:1824;;;;;1729:32;289:1824;;;;;;;;;;-1:-1:-1;;289:1824:40;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;289:1824:40;;;;;;;914:18;289:1824;;;;;;910:93;;289:1824;;;;;;;;;;;;;;;;;;;;;;1047:239;;289:1824;;;;;;;1047:239;;289:1824;;;1047:239;;289:1824;;;;1047:239;;289:1824;-1:-1:-1;;;;;;;;;;;289:1824:40;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;289:1824:40;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1047:239;;289:1824;;;;;;1047:239;;289:1824;;;;;1047:239;289:1824;;;;-1:-1:-1;;;;;;;;;;;289:1824:40;;;;;;914:18;289:1824;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;1371:80;289:1824;;;;;;;;;;;;1371:80;;289:1824;;;;;-1:-1:-1;289:1824:40;;;;;;;;;;;;;;;;;-1:-1:-1;;289:1824:40;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1013:15;289:1824;;;;;;;;;;;;1013:15;289:1824;;;;;;;;;;;;;;;;;;;;-1:-1:-1;289:1824:40;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;910:93;289:1824;;-1:-1:-1;;;964:28:40;;289:1824;;;;964:28;;289:1824;;;964:28;289:1824;;;;-1:-1:-1;;289:1824:40;;;;;;:::o;:::-;-1:-1:-1;;;;;;;;;;;289:1824:40;;;;;;;;;;-1:-1:-1;289:1824:40;;;;;-1:-1:-1;289:1824:40;:::o;:::-;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;289:1824:40;;;;:::o;:::-;;;;;;;;;;;;", - "linkReferences": {} - }, - "methodIdentifiers": { - "adjustAuthorizedShares(bytes16,uint256)": "67ede628", - "createStockClass(bytes16,string,uint256,uint256)": "4afd58fa" - }, - "rawMetadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"InvalidSharesAuthorized\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stock_class_id\",\"type\":\"bytes16\"}],\"name\":\"StockClassAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stock_class_id\",\"type\":\"bytes16\"}],\"name\":\"StockClassNotFound\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes16\",\"name\":\"stockClassId\",\"type\":\"bytes16\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newSharesAuthorized\",\"type\":\"uint256\"}],\"name\":\"StockClassAuthorizedSharesAdjusted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes16\",\"name\":\"id\",\"type\":\"bytes16\"},{\"indexed\":true,\"internalType\":\"string\",\"name\":\"classType\",\"type\":\"string\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"pricePerShare\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"initialSharesAuthorized\",\"type\":\"uint256\"}],\"name\":\"StockClassCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"enum TxType\",\"name\":\"txType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"txData\",\"type\":\"bytes\"}],\"name\":\"TxCreated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stockClassId\",\"type\":\"bytes16\"},{\"internalType\":\"uint256\",\"name\":\"newSharesAuthorized\",\"type\":\"uint256\"}],\"name\":\"adjustAuthorizedShares\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"_id\",\"type\":\"bytes16\"},{\"internalType\":\"string\",\"name\":\"_class_type\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"_price_per_share\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_initial_share_authorized\",\"type\":\"uint256\"}],\"name\":\"createStockClass\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/lib/diamond/facets/StockClassFacet.sol\":\"StockClassFacet\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"appendCBOR\":false,\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@diamond/=src/lib/diamond/\",\":diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/\",\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\",\":openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/\"],\"viaIR\":true},\"sources\":{\"lib/diamond-3-hardhat/contracts/interfaces/IDiamondCut.sol\":{\"keccak256\":\"0xc00c16bfa30a3fa5f3dc684f7f8ba62c259962b25f647d9588739458989717fc\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://119d9a5acd99b9462a8341c9b95ddd468648799eefa47038f81521431743c1ae\",\"dweb:/ipfs/QmTF7WNyPWTUtUzNcpq5rf5v2uw5TwzqsSg9D53pfQufcu\"]},\"lib/diamond-3-hardhat/contracts/libraries/LibDiamond.sol\":{\"keccak256\":\"0xc5044f5a7a031e4e1869a26addf83b25c8b20d5949ba13b613dfbc72ad2f63b0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://25f2ac88867d97bfd13b503c06512b082ecf861f3f00702ca2747502c9113a79\",\"dweb:/ipfs/QmSYDNeaLGXDsPa2maoaeim5qJiLNuM3PbDguoYmsUmAZL\"]},\"src/lib/Structs.sol\":{\"keccak256\":\"0x404e740ae677baf5cc57884ee32d9accb367ae58f56a23f4e59b4f2987ae5100\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://349ec5d7e23684f71836001d70c012e59c0f2c94c9fa1fae83f85eeccda1fe52\",\"dweb:/ipfs/QmTVT5hzGePT8FxFxWhX77bH1DS1Z8iu8NAfAdn4PfsyCh\"]},\"src/lib/diamond/DiamondTxHelper.sol\":{\"keccak256\":\"0xe72ba093a5b16735d0193624d5b6585deb2cfe7e419a468c90fd70317fccf5e6\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://4ae3bd1ccbe005c63bed07e255c2b25c9bfdb37b9d85bb75f8f65cfeb0f6df98\",\"dweb:/ipfs/QmbTpzVeLkvthzpasQLvuvAfwE3g4sMsjEfpRBNwyG4eMQ\"]},\"src/lib/diamond/Storage.sol\":{\"keccak256\":\"0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d\",\"dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL\"]},\"src/lib/diamond/Structs.sol\":{\"keccak256\":\"0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab\",\"dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD\"]},\"src/lib/diamond/facets/StockClassFacet.sol\":{\"keccak256\":\"0x3748ae9ad8b1038e75af76911a9e41472428685da93aa0ed754ac7755741305f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7b930f82dfa1901b5943fc1a2df406b2ccb52da5e7526f2d9a71beb08c562d13\",\"dweb:/ipfs/QmRDeck7qdyqeHR5BMBzHfv32jkjG51FHTmgjzgs7Q2NHX\"]}},\"version\":1}", - "metadata": { - "compiler": { - "version": "0.8.20+commit.a1b79de6" - }, - "language": "Solidity", - "output": { - "abi": [ - { - "inputs": [], - "type": "error", - "name": "InvalidSharesAuthorized" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stock_class_id", - "type": "bytes16" - } - ], - "type": "error", - "name": "StockClassAlreadyExists" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stock_class_id", - "type": "bytes16" - } - ], - "type": "error", - "name": "StockClassNotFound" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stockClassId", - "type": "bytes16", - "indexed": true - }, - { - "internalType": "uint256", - "name": "newSharesAuthorized", - "type": "uint256", - "indexed": false - } - ], - "type": "event", - "name": "StockClassAuthorizedSharesAdjusted", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "id", - "type": "bytes16", - "indexed": true - }, - { - "internalType": "string", - "name": "classType", - "type": "string", - "indexed": true - }, - { - "internalType": "uint256", - "name": "pricePerShare", - "type": "uint256", - "indexed": true - }, - { - "internalType": "uint256", - "name": "initialSharesAuthorized", - "type": "uint256", - "indexed": false - } - ], - "type": "event", - "name": "StockClassCreated", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "enum TxType", - "name": "txType", - "type": "uint8", - "indexed": false - }, - { - "internalType": "bytes", - "name": "txData", - "type": "bytes", - "indexed": false - } - ], - "type": "event", - "name": "TxCreated", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stockClassId", - "type": "bytes16" - }, - { - "internalType": "uint256", - "name": "newSharesAuthorized", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "adjustAuthorizedShares" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "_id", - "type": "bytes16" - }, - { - "internalType": "string", - "name": "_class_type", - "type": "string" - }, - { - "internalType": "uint256", - "name": "_price_per_share", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_initial_share_authorized", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "createStockClass" - } - ], - "devdoc": { - "kind": "dev", - "methods": {}, - "version": 1 - }, - "userdoc": { - "kind": "user", - "methods": {}, - "version": 1 - } - }, - "settings": { - "remappings": [ - "@diamond/=src/lib/diamond/", - "diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/", - "ds-test/=lib/forge-std/lib/ds-test/src/", - "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", - "forge-std/=lib/forge-std/src/", - "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", - "openzeppelin-contracts/=lib/openzeppelin-contracts/", - "openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/" - ], - "optimizer": { - "enabled": true, - "runs": 200 - }, - "metadata": { - "bytecodeHash": "none", - "appendCBOR": false - }, - "compilationTarget": { - "src/lib/diamond/facets/StockClassFacet.sol": "StockClassFacet" - }, - "evmVersion": "paris", - "libraries": {}, - "viaIR": true - }, - "sources": { - "lib/diamond-3-hardhat/contracts/interfaces/IDiamondCut.sol": { - "keccak256": "0xc00c16bfa30a3fa5f3dc684f7f8ba62c259962b25f647d9588739458989717fc", - "urls": [ - "bzz-raw://119d9a5acd99b9462a8341c9b95ddd468648799eefa47038f81521431743c1ae", - "dweb:/ipfs/QmTF7WNyPWTUtUzNcpq5rf5v2uw5TwzqsSg9D53pfQufcu" - ], - "license": "MIT" - }, - "lib/diamond-3-hardhat/contracts/libraries/LibDiamond.sol": { - "keccak256": "0xc5044f5a7a031e4e1869a26addf83b25c8b20d5949ba13b613dfbc72ad2f63b0", - "urls": [ - "bzz-raw://25f2ac88867d97bfd13b503c06512b082ecf861f3f00702ca2747502c9113a79", - "dweb:/ipfs/QmSYDNeaLGXDsPa2maoaeim5qJiLNuM3PbDguoYmsUmAZL" - ], - "license": "MIT" - }, - "src/lib/Structs.sol": { - "keccak256": "0x404e740ae677baf5cc57884ee32d9accb367ae58f56a23f4e59b4f2987ae5100", - "urls": [ - "bzz-raw://349ec5d7e23684f71836001d70c012e59c0f2c94c9fa1fae83f85eeccda1fe52", - "dweb:/ipfs/QmTVT5hzGePT8FxFxWhX77bH1DS1Z8iu8NAfAdn4PfsyCh" - ], - "license": "MIT" - }, - "src/lib/diamond/DiamondTxHelper.sol": { - "keccak256": "0xe72ba093a5b16735d0193624d5b6585deb2cfe7e419a468c90fd70317fccf5e6", - "urls": [ - "bzz-raw://4ae3bd1ccbe005c63bed07e255c2b25c9bfdb37b9d85bb75f8f65cfeb0f6df98", - "dweb:/ipfs/QmbTpzVeLkvthzpasQLvuvAfwE3g4sMsjEfpRBNwyG4eMQ" - ], - "license": "MIT" - }, - "src/lib/diamond/Storage.sol": { - "keccak256": "0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f", - "urls": [ - "bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d", - "dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL" - ], - "license": "MIT" - }, - "src/lib/diamond/Structs.sol": { - "keccak256": "0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2", - "urls": [ - "bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab", - "dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/StockClassFacet.sol": { - "keccak256": "0x3748ae9ad8b1038e75af76911a9e41472428685da93aa0ed754ac7755741305f", - "urls": [ - "bzz-raw://7b930f82dfa1901b5943fc1a2df406b2ccb52da5e7526f2d9a71beb08c562d13", - "dweb:/ipfs/QmRDeck7qdyqeHR5BMBzHfv32jkjG51FHTmgjzgs7Q2NHX" - ], - "license": "MIT" - } - }, - "version": 1 - }, - "id": 40 -} \ No newline at end of file diff --git a/chain/out/StockFacet.sol/StockFacet.json b/chain/out/StockFacet.sol/StockFacet.json deleted file mode 100644 index 3c7e0c6a..00000000 --- a/chain/out/StockFacet.sol/StockFacet.json +++ /dev/null @@ -1,366 +0,0 @@ -{ - "abi": [ - { - "type": "function", - "name": "getStockPosition", - "inputs": [ - { - "name": "securityId", - "type": "bytes16", - "internalType": "bytes16" - } - ], - "outputs": [ - { - "name": "", - "type": "tuple", - "internalType": "struct StockActivePosition", - "components": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "stock_class_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "quantity", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "share_price", - "type": "uint256", - "internalType": "uint256" - } - ] - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "issueStock", - "inputs": [ - { - "name": "stock_class_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "share_price", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "quantity", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "security_id", - "type": "bytes16", - "internalType": "bytes16" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "event", - "name": "TxCreated", - "inputs": [ - { - "name": "txType", - "type": "uint8", - "indexed": false, - "internalType": "enum TxType" - }, - { - "name": "txData", - "type": "bytes", - "indexed": false, - "internalType": "bytes" - } - ], - "anonymous": false - }, - { - "type": "error", - "name": "InvalidAmount", - "inputs": [] - }, - { - "type": "error", - "name": "InvalidQuantity", - "inputs": [] - }, - { - "type": "error", - "name": "InvalidStockClass", - "inputs": [ - { - "name": "stock_class_id", - "type": "bytes16", - "internalType": "bytes16" - } - ] - }, - { - "type": "error", - "name": "NoStakeholder", - "inputs": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - } - ] - } - ], - "bytecode": { - "object": "0x60808060405234610016576106dc908161001c8239f35b600080fdfe608060408181526004918236101561001657600080fd5b600092833560e01c91826355356b8b146101015750506393d14df11461003b57600080fd5b346100fd5760203660031901126100fd5790816080926100596105fb565b92806060835161006881610687565b828152826020820152828582015201526001600160801b031980941681527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfe60205220908051916100b883610687565b8054938085871b169485855281602086019116815260606002600185015494868801958652015495019485528351958652511660208501525190830152516060820152f35b5080fd5b90929150346105f75760a03660031901126105f75761011e6105fb565b60643592906001600160801b031990818516908186036105f35760843590838216938483036105ef577f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfc80546000199391908481146105dc576001019055848b526020977f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf78952878c2054156105c75750811697888b527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf991828952878c2054156105b0576044359485156105a05760243594851561059057898e8d8152868d52205481810190811161057c5761021490610617565b50947f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf4956102438988546106b9565b7f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf5541061052b578e6102798a60038401546106b9565b910154106104d5578c8f528b52898e20549081019081116104c25790898e8960028f8f8b918f9a99988e926102ce7f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfe92610617565b509c8951976102dc89610687565b8852818801938452898801948552606088019586528852528887872095519151169060801c178455516001840155519101558981527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfd8d522090815491680100000000000000008310156104ad5760018301808255831015610498578f90928c928e600398979695845283208260011c01916001600160801b03608084549260071b169260801c831b921b19161790558a81527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bff8d52209160801c90825416179055016103ca8482546106b9565b90556103d78382546106b9565b905584519686880152848701526060860152608085015260a084015260a0835260c083019383851067ffffffffffffffff86111761048557508381526005845260e083015281519081610100840152845b8281106104705782840161012001869052857f88aa35e399ceb8d6b2e810abb3df607c140128e7c089c428f212598bda6a7eb7601f8501601f1916860186900360600187a180f35b80829185016101208382015191015201610428565b634e487b7160e01b865260419052602485fd5b5050634e487b7160e01b8e5260328d5260248efd5b5050634e487b7160e01b8e5260418d5260248efd5b634e487b7160e01b8e5260118d5260248efd5b8a5162461bcd60e51b8152808f018d9052602a60248201527f53746f636b436c6173733a20496e73756666696369656e742073686172657320604482015269185d5d1a1bdc9a5e995960b21b6064820152608490fd5b60848f8e8e519162461bcd60e51b8352820152602660248201527f4973737565723a20496e73756666696369656e742073686172657320617574686044820152651bdc9a5e995960d21b6064820152fd5b50634e487b7160e01b8e5260118d5260248efd5b895163162908e360e11b81528d90fd5b885163524f409b60e01b81528c90fd5b8751630b2b152360e21b8152808c018b9052602490fd5b63b4586dfb60e01b81528a8101869052602490fd5b634e487b7160e01b8d5260118c5260248dfd5b8980fd5b8780fd5b8380fd5b600435906001600160801b03198216820361061257565b600080fd5b7f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf890815481101561067157600591600052027fee6f7b31ced919e4b28e9b6e4406f8a625ca3bdeb0e9e0c4c4bc61059574b3180190600090565b634e487b7160e01b600052603260045260246000fd5b6080810190811067ffffffffffffffff8211176106a357604052565b634e487b7160e01b600052604160045260246000fd5b919082018092116106c657565b634e487b7160e01b600052601160045260246000fd", - "sourceMap": "301:1892:74:-:0;;;;;;;;;;;;;;;;;", - "linkReferences": {} - }, - "deployedBytecode": { - "object": "0x608060408181526004918236101561001657600080fd5b600092833560e01c91826355356b8b146101015750506393d14df11461003b57600080fd5b346100fd5760203660031901126100fd5790816080926100596105fb565b92806060835161006881610687565b828152826020820152828582015201526001600160801b031980941681527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfe60205220908051916100b883610687565b8054938085871b169485855281602086019116815260606002600185015494868801958652015495019485528351958652511660208501525190830152516060820152f35b5080fd5b90929150346105f75760a03660031901126105f75761011e6105fb565b60643592906001600160801b031990818516908186036105f35760843590838216938483036105ef577f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfc80546000199391908481146105dc576001019055848b526020977f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf78952878c2054156105c75750811697888b527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf991828952878c2054156105b0576044359485156105a05760243594851561059057898e8d8152868d52205481810190811161057c5761021490610617565b50947f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf4956102438988546106b9565b7f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf5541061052b578e6102798a60038401546106b9565b910154106104d5578c8f528b52898e20549081019081116104c25790898e8960028f8f8b918f9a99988e926102ce7f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfe92610617565b509c8951976102dc89610687565b8852818801938452898801948552606088019586528852528887872095519151169060801c178455516001840155519101558981527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfd8d522090815491680100000000000000008310156104ad5760018301808255831015610498578f90928c928e600398979695845283208260011c01916001600160801b03608084549260071b169260801c831b921b19161790558a81527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bff8d52209160801c90825416179055016103ca8482546106b9565b90556103d78382546106b9565b905584519686880152848701526060860152608085015260a084015260a0835260c083019383851067ffffffffffffffff86111761048557508381526005845260e083015281519081610100840152845b8281106104705782840161012001869052857f88aa35e399ceb8d6b2e810abb3df607c140128e7c089c428f212598bda6a7eb7601f8501601f1916860186900360600187a180f35b80829185016101208382015191015201610428565b634e487b7160e01b865260419052602485fd5b5050634e487b7160e01b8e5260328d5260248efd5b5050634e487b7160e01b8e5260418d5260248efd5b634e487b7160e01b8e5260118d5260248efd5b8a5162461bcd60e51b8152808f018d9052602a60248201527f53746f636b436c6173733a20496e73756666696369656e742073686172657320604482015269185d5d1a1bdc9a5e995960b21b6064820152608490fd5b60848f8e8e519162461bcd60e51b8352820152602660248201527f4973737565723a20496e73756666696369656e742073686172657320617574686044820152651bdc9a5e995960d21b6064820152fd5b50634e487b7160e01b8e5260118d5260248efd5b895163162908e360e11b81528d90fd5b885163524f409b60e01b81528c90fd5b8751630b2b152360e21b8152808c018b9052602490fd5b63b4586dfb60e01b81528a8101869052602490fd5b634e487b7160e01b8d5260118c5260248dfd5b8980fd5b8780fd5b8380fd5b600435906001600160801b03198216820361061257565b600080fd5b7f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf890815481101561067157600591600052027fee6f7b31ced919e4b28e9b6e4406f8a625ca3bdeb0e9e0c4c4bc61059574b3180190600090565b634e487b7160e01b600052603260045260246000fd5b6080810190811067ffffffffffffffff8211176106a357604052565b634e487b7160e01b600052604160045260246000fd5b919082018092116106c657565b634e487b7160e01b600052601160045260246000fd", - "sourceMap": "301:1892:74:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;301:1892:74;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;301:1892:74;;;;;;2138:34;301:1892;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;2138:34;301:1892;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;301:1892:74;;;;;;:::i;:::-;;;;;-1:-1:-1;;;;;;301:1892:74;;;;;;;;;;;;;;;;;;;;;;521:8;301:1892;;-1:-1:-1;;301:1892:74;;521:8;301:1892;;;;;;;;;;;;;;686:19:77;301:1892:74;;;;;;686:40:77;682:107;;301:1892:74;;;;;;;928:18:77;301:1892:74;;;;;;;;928:39:77;924:110;;301:1892:74;;1357:13:77;;;1353:43;;301:1892:74;;1477:11:77;;;1473:39;;301:1892:74;;;;;;;;;;;;;;;;;;1775:30:77;;;:::i;:::-;1824:23;;;301:1892:74;1824:34:77;301:1892:74;;;1824:34:77;:::i;:::-;1862:27;301:1892:74;-1:-1:-1;301:1892:74;;1950:24:77;:35;:24;301:1892:74;1950:24:77;;301:1892:74;1950:35:77;:::i;:::-;1989:28;;301:1892:74;-1:-1:-1;301:1892:74;;;;;;;;;;;;;;;;;;;984:30;;;;1862:27:77;984:30:74;;;;;;;;;;;1062:34;984:30;;:::i;:::-;301:1892;;;;;;;;:::i;:::-;;;1112:189;;;301:1892;;;1112:189;;;301:1892;;;;1112:189;;301:1892;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1062:23;301:1892;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;301:1892:74;;;;;;;;;;;;;;;;;;;;;;1486:45;301:1892;;;;;;;;;;;;;1603:24;:36;301:1892;;;1603:36;:::i;:::-;301:1892;;1649:35;301:1892;;;1649:35;:::i;:::-;301:1892;;;;1828:78;;;;301:1892;;;;;;;;;;;;;;;;;;1828:78;;301:1892;;;;;;;;;;;;;;;;;1775:15:77;301:1892:74;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1098:25:65;301:1892:74;;;-1:-1:-1;;301:1892:74;;;1098:25:65;;;301:1892:74;1098:25:65;301:1892:74;1098:25:65;301:1892:74;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;301:1892:74;;;;;;;;;-1:-1:-1;;;;;301:1892:74;;;;;;;;;-1:-1:-1;;;;;301:1892:74;;;;;;;;;-1:-1:-1;;;301:1892:74;;;;;;;;;;;-1:-1:-1;;;301:1892:74;;;;;;;;;;;;;;;;;;-1:-1:-1;;;301:1892:74;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;301:1892:74;;;;;;-1:-1:-1;;;;301:1892:74;;;;;;;;1473:39:77;301:1892:74;;-1:-1:-1;;;1497:15:77;;301:1892:74;;1497:15:77;1353:43;301:1892:74;;-1:-1:-1;;;1379:17:77;;301:1892:74;;1379:17:77;924:110;301:1892:74;;-1:-1:-1;;;990:33:77;;;;;301:1892:74;;;;;990:33:77;682:107;-1:-1:-1;;;749:29:77;;;;;301:1892:74;;;;;749:29:77;301:1892:74;-1:-1:-1;;;301:1892:74;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;301:1892:74;;;;;;:::o;:::-;;;;;1775:15:77;301:1892:74;;;;;;;;;;-1:-1:-1;301:1892:74;;;;;-1:-1:-1;301:1892:74;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;", - "linkReferences": {} - }, - "methodIdentifiers": { - "getStockPosition(bytes16)": "93d14df1", - "issueStock(bytes16,uint256,uint256,bytes16,bytes16)": "55356b8b" - }, - "rawMetadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"InvalidAmount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidQuantity\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stock_class_id\",\"type\":\"bytes16\"}],\"name\":\"InvalidStockClass\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"}],\"name\":\"NoStakeholder\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"enum TxType\",\"name\":\"txType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"txData\",\"type\":\"bytes\"}],\"name\":\"TxCreated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"securityId\",\"type\":\"bytes16\"}],\"name\":\"getStockPosition\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"},{\"internalType\":\"bytes16\",\"name\":\"stock_class_id\",\"type\":\"bytes16\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"share_price\",\"type\":\"uint256\"}],\"internalType\":\"struct StockActivePosition\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stock_class_id\",\"type\":\"bytes16\"},{\"internalType\":\"uint256\",\"name\":\"share_price\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"},{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"},{\"internalType\":\"bytes16\",\"name\":\"security_id\",\"type\":\"bytes16\"}],\"name\":\"issueStock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/lib/diamond/facets/StockFacet.sol\":\"StockFacet\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"appendCBOR\":false,\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@diamond/=src/lib/diamond/\",\":diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/\",\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\",\":openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/\"],\"viaIR\":true},\"sources\":{\"src/lib/Structs.sol\":{\"keccak256\":\"0x404e740ae677baf5cc57884ee32d9accb367ae58f56a23f4e59b4f2987ae5100\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://349ec5d7e23684f71836001d70c012e59c0f2c94c9fa1fae83f85eeccda1fe52\",\"dweb:/ipfs/QmTVT5hzGePT8FxFxWhX77bH1DS1Z8iu8NAfAdn4PfsyCh\"]},\"src/lib/diamond/DiamondTxHelper.sol\":{\"keccak256\":\"0xe72ba093a5b16735d0193624d5b6585deb2cfe7e419a468c90fd70317fccf5e6\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://4ae3bd1ccbe005c63bed07e255c2b25c9bfdb37b9d85bb75f8f65cfeb0f6df98\",\"dweb:/ipfs/QmbTpzVeLkvthzpasQLvuvAfwE3g4sMsjEfpRBNwyG4eMQ\"]},\"src/lib/diamond/Storage.sol\":{\"keccak256\":\"0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d\",\"dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL\"]},\"src/lib/diamond/Structs.sol\":{\"keccak256\":\"0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab\",\"dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD\"]},\"src/lib/diamond/facets/StockFacet.sol\":{\"keccak256\":\"0xc6765b22e33ccd04c7c5f65cd2a33a5eb18c9199be9ff941784a3f458445161a\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://99eb3c365628ca6a37b9851373d7570ed3cafa9a43f0809754bedbcebcb0b200\",\"dweb:/ipfs/QmQE93qCuuWdWZahUCrtEuAWDbKwUKRQC1726yqUPN9WCi\"]},\"src/lib/diamond/libraries/ValidationLib.sol\":{\"keccak256\":\"0xfcd772b0670acded810468d772729715e3cd4db25c02a0da77c7445903e1cc5f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d572672df16392825ed86c32a53453247f62b481787db0498993f857623e1ee6\",\"dweb:/ipfs/Qmbpoa5EZ7yRcB1mZkmb5nhfxhhFY9g8tMJxPf9ZrzfNfL\"]}},\"version\":1}", - "metadata": { - "compiler": { - "version": "0.8.20+commit.a1b79de6" - }, - "language": "Solidity", - "output": { - "abi": [ - { - "inputs": [], - "type": "error", - "name": "InvalidAmount" - }, - { - "inputs": [], - "type": "error", - "name": "InvalidQuantity" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stock_class_id", - "type": "bytes16" - } - ], - "type": "error", - "name": "InvalidStockClass" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - } - ], - "type": "error", - "name": "NoStakeholder" - }, - { - "inputs": [ - { - "internalType": "enum TxType", - "name": "txType", - "type": "uint8", - "indexed": false - }, - { - "internalType": "bytes", - "name": "txData", - "type": "bytes", - "indexed": false - } - ], - "type": "event", - "name": "TxCreated", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "securityId", - "type": "bytes16" - } - ], - "stateMutability": "view", - "type": "function", - "name": "getStockPosition", - "outputs": [ - { - "internalType": "struct StockActivePosition", - "name": "", - "type": "tuple", - "components": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - }, - { - "internalType": "bytes16", - "name": "stock_class_id", - "type": "bytes16" - }, - { - "internalType": "uint256", - "name": "quantity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "share_price", - "type": "uint256" - } - ] - } - ] - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stock_class_id", - "type": "bytes16" - }, - { - "internalType": "uint256", - "name": "share_price", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "quantity", - "type": "uint256" - }, - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - }, - { - "internalType": "bytes16", - "name": "security_id", - "type": "bytes16" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "issueStock" - } - ], - "devdoc": { - "kind": "dev", - "methods": {}, - "version": 1 - }, - "userdoc": { - "kind": "user", - "methods": {}, - "version": 1 - } - }, - "settings": { - "remappings": [ - "@diamond/=src/lib/diamond/", - "diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/", - "ds-test/=lib/forge-std/lib/ds-test/src/", - "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", - "forge-std/=lib/forge-std/src/", - "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", - "openzeppelin-contracts/=lib/openzeppelin-contracts/", - "openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/" - ], - "optimizer": { - "enabled": true, - "runs": 200 - }, - "metadata": { - "bytecodeHash": "none", - "appendCBOR": false - }, - "compilationTarget": { - "src/lib/diamond/facets/StockFacet.sol": "StockFacet" - }, - "evmVersion": "paris", - "libraries": {}, - "viaIR": true - }, - "sources": { - "src/lib/Structs.sol": { - "keccak256": "0x404e740ae677baf5cc57884ee32d9accb367ae58f56a23f4e59b4f2987ae5100", - "urls": [ - "bzz-raw://349ec5d7e23684f71836001d70c012e59c0f2c94c9fa1fae83f85eeccda1fe52", - "dweb:/ipfs/QmTVT5hzGePT8FxFxWhX77bH1DS1Z8iu8NAfAdn4PfsyCh" - ], - "license": "MIT" - }, - "src/lib/diamond/DiamondTxHelper.sol": { - "keccak256": "0xe72ba093a5b16735d0193624d5b6585deb2cfe7e419a468c90fd70317fccf5e6", - "urls": [ - "bzz-raw://4ae3bd1ccbe005c63bed07e255c2b25c9bfdb37b9d85bb75f8f65cfeb0f6df98", - "dweb:/ipfs/QmbTpzVeLkvthzpasQLvuvAfwE3g4sMsjEfpRBNwyG4eMQ" - ], - "license": "MIT" - }, - "src/lib/diamond/Storage.sol": { - "keccak256": "0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f", - "urls": [ - "bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d", - "dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL" - ], - "license": "MIT" - }, - "src/lib/diamond/Structs.sol": { - "keccak256": "0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2", - "urls": [ - "bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab", - "dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/StockFacet.sol": { - "keccak256": "0xc6765b22e33ccd04c7c5f65cd2a33a5eb18c9199be9ff941784a3f458445161a", - "urls": [ - "bzz-raw://99eb3c365628ca6a37b9851373d7570ed3cafa9a43f0809754bedbcebcb0b200", - "dweb:/ipfs/QmQE93qCuuWdWZahUCrtEuAWDbKwUKRQC1726yqUPN9WCi" - ], - "license": "MIT" - }, - "src/lib/diamond/libraries/ValidationLib.sol": { - "keccak256": "0xfcd772b0670acded810468d772729715e3cd4db25c02a0da77c7445903e1cc5f", - "urls": [ - "bzz-raw://d572672df16392825ed86c32a53453247f62b481787db0498993f857623e1ee6", - "dweb:/ipfs/Qmbpoa5EZ7yRcB1mZkmb5nhfxhhFY9g8tMJxPf9ZrzfNfL" - ], - "license": "MIT" - } - }, - "version": 1 - }, - "id": 74 -} \ No newline at end of file diff --git a/chain/out/StockPlanFacet.sol/StockPlanFacet.json b/chain/out/StockPlanFacet.sol/StockPlanFacet.json deleted file mode 100644 index 1271f83a..00000000 --- a/chain/out/StockPlanFacet.sol/StockPlanFacet.json +++ /dev/null @@ -1,385 +0,0 @@ -{ - "abi": [ - { - "type": "function", - "name": "adjustStockPlanPool", - "inputs": [ - { - "name": "stockPlanId", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "newSharesReserved", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "createStockPlan", - "inputs": [ - { - "name": "_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "_stock_class_ids", - "type": "bytes16[]", - "internalType": "bytes16[]" - }, - { - "name": "_shares_reserved", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "event", - "name": "StockPlanCreated", - "inputs": [ - { - "name": "id", - "type": "bytes16", - "indexed": true, - "internalType": "bytes16" - }, - { - "name": "shares_reserved", - "type": "uint256", - "indexed": false, - "internalType": "uint256" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "StockPlanSharesReservedAdjusted", - "inputs": [ - { - "name": "id", - "type": "bytes16", - "indexed": true, - "internalType": "bytes16" - }, - { - "name": "newSharesReserved", - "type": "uint256", - "indexed": false, - "internalType": "uint256" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "TxCreated", - "inputs": [ - { - "name": "txType", - "type": "uint8", - "indexed": false, - "internalType": "enum TxType" - }, - { - "name": "txData", - "type": "bytes", - "indexed": false, - "internalType": "bytes" - } - ], - "anonymous": false - }, - { - "type": "error", - "name": "InvalidStockClass", - "inputs": [ - { - "name": "stock_class_id", - "type": "bytes16", - "internalType": "bytes16" - } - ] - }, - { - "type": "error", - "name": "StockPlanAlreadyExists", - "inputs": [ - { - "name": "stock_plan_id", - "type": "bytes16", - "internalType": "bytes16" - } - ] - }, - { - "type": "error", - "name": "StockPlanNotFound", - "inputs": [ - { - "name": "stock_plan_id", - "type": "bytes16", - "internalType": "bytes16" - } - ] - } - ], - "bytecode": { - "object": "0x6080806040523461001657610698908161001c8239f35b600080fdfe608080604052600436101561001357600080fd5b60003560e01c9081636b348f411461018f575063e1c5951c1461003557600080fd5b3461018a57604036600319011261018a5761004e6105f0565b602435906001600160801b031916806000526020907f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfb82526040600020549081156101725750600019810190811161015c5760016100ac8492610631565b5001556040519181830152808252604082019082821067ffffffffffffffff8311176101465781604052600c82526040606084015282519081608085015260005b82811061013257600083860160a001527f88aa35e399ceb8d6b2e810abb3df607c140128e7c089c428f212598bda6a7eb7601f8401601f1916860186900360600185a1005b808291860160a083820151910152016100ed565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60249060405190632148b60160e01b82526004820152fd5b600080fd5b3461018a57606036600319011261018a576101a86105f0565b60249167ffffffffffffffff9190833583811161018a573660238201121561018a578060040135908482116105db578160051b91601f19603f8401168401848110878211176105c65760405283528560208401928201019036821161018a578601915b8183106105a5575050506001600160801b0319918281166000527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfb60205260406000205461058c5760005b82518110156102e7578361026a8285610607565b51166000527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf9602052604060002054156102c45760001981146102af57600101610256565b85634e487b7160e01b60005260116004526000fd5b836102d0879285610607565b51604051630b2b152360e21b815291166004820152fd5b509391926040519160408301838110838211176105775760405282526044356020830152600080516020610678833981519152546801000000000000000090818110156105775780600161034b920160008051602061067883398151915255610631565b94909461056357835191825193841161054e57831161053a575083548285558083106104de575b50602001908360005260206000208160011c9160005b83811061048f57506001198116900380610419575b877f62cb99f09061d2ccca9332015b4b6fc4feca38d02e058bb0683bb8ebed934cf0602089896001838b0151910155600080516020610678833981519152548185166000527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfb83526040600020556040519360443585521692a2005b9260009360005b81811061045757505050015581600160207f62cb99f09061d2ccca9332015b4b6fc4feca38d02e058bb0683bb8ebed934cf061039d565b9091946020610485600192885160801c908560041b60031b916001600160801b03809116831b921b19161790565b9601929101610420565b6000805b600281106104a8575083820155600101610388565b959060206104d5600192845160801c908a60041b60031b916001600160801b03809116831b921b19161790565b92019601610493565b8460005260206000206001808501811c8201920160011c019060108460041b168061051f575b505b8181106105135750610372565b60008155600101610506565b600019908183019182549160200360031b1c16905588610504565b634e487b7160e01b60009081526041600452fd5b50634e487b7160e01b60009081526041600452fd5b634e487b7160e01b60005260006004526000fd5b84634e487b7160e01b60005260416004526000fd5b60405163b8f59a7760e01b815292166004830152509050fd5b82356001600160801b03198116810361018a5781526020928301920161020b565b87634e487b7160e01b60005260416004526000fd5b85634e487b7160e01b60005260416004526000fd5b600435906001600160801b03198216820361018a57565b805182101561061b5760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b600080516020610678833981519152805482101561061b5760005260011b7f0de091a2bf9cd02c57bf491faca384ed63a687285048f1ca1a1538d6eb308ac0019060009056fe2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfa", - "sourceMap": "288:1635:42:-:0;;;;;;;;;;;;;;;;;", - "linkReferences": {} - }, - "deployedBytecode": { - "object": "0x608080604052600436101561001357600080fd5b60003560e01c9081636b348f411461018f575063e1c5951c1461003557600080fd5b3461018a57604036600319011261018a5761004e6105f0565b602435906001600160801b031916806000526020907f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfb82526040600020549081156101725750600019810190811161015c5760016100ac8492610631565b5001556040519181830152808252604082019082821067ffffffffffffffff8311176101465781604052600c82526040606084015282519081608085015260005b82811061013257600083860160a001527f88aa35e399ceb8d6b2e810abb3df607c140128e7c089c428f212598bda6a7eb7601f8401601f1916860186900360600185a1005b808291860160a083820151910152016100ed565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60249060405190632148b60160e01b82526004820152fd5b600080fd5b3461018a57606036600319011261018a576101a86105f0565b60249167ffffffffffffffff9190833583811161018a573660238201121561018a578060040135908482116105db578160051b91601f19603f8401168401848110878211176105c65760405283528560208401928201019036821161018a578601915b8183106105a5575050506001600160801b0319918281166000527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfb60205260406000205461058c5760005b82518110156102e7578361026a8285610607565b51166000527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf9602052604060002054156102c45760001981146102af57600101610256565b85634e487b7160e01b60005260116004526000fd5b836102d0879285610607565b51604051630b2b152360e21b815291166004820152fd5b509391926040519160408301838110838211176105775760405282526044356020830152600080516020610678833981519152546801000000000000000090818110156105775780600161034b920160008051602061067883398151915255610631565b94909461056357835191825193841161054e57831161053a575083548285558083106104de575b50602001908360005260206000208160011c9160005b83811061048f57506001198116900380610419575b877f62cb99f09061d2ccca9332015b4b6fc4feca38d02e058bb0683bb8ebed934cf0602089896001838b0151910155600080516020610678833981519152548185166000527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfb83526040600020556040519360443585521692a2005b9260009360005b81811061045757505050015581600160207f62cb99f09061d2ccca9332015b4b6fc4feca38d02e058bb0683bb8ebed934cf061039d565b9091946020610485600192885160801c908560041b60031b916001600160801b03809116831b921b19161790565b9601929101610420565b6000805b600281106104a8575083820155600101610388565b959060206104d5600192845160801c908a60041b60031b916001600160801b03809116831b921b19161790565b92019601610493565b8460005260206000206001808501811c8201920160011c019060108460041b168061051f575b505b8181106105135750610372565b60008155600101610506565b600019908183019182549160200360031b1c16905588610504565b634e487b7160e01b60009081526041600452fd5b50634e487b7160e01b60009081526041600452fd5b634e487b7160e01b60005260006004526000fd5b84634e487b7160e01b60005260416004526000fd5b60405163b8f59a7760e01b815292166004830152509050fd5b82356001600160801b03198116810361018a5781526020928301920161020b565b87634e487b7160e01b60005260416004526000fd5b85634e487b7160e01b60005260416004526000fd5b600435906001600160801b03198216820361018a57565b805182101561061b5760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b600080516020610678833981519152805482101561061b5760005260011b7f0de091a2bf9cd02c57bf491faca384ed63a687285048f1ca1a1538d6eb308ac0019060009056fe2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfa", - "sourceMap": "288:1635:42:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;288:1635:42;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;288:1635:42;;;;;;;1564:17;288:1635;;;;;;1609:19;;;1605:87;;-1:-1:-1;;;288:1635:42;;;;;;;;1732:33;;;;:::i;:::-;1775:25;;288:1635;;;1884:29;;;;288:1635;1884:29;;;288:1635;;;;;;;;;;;;;;;;1849:33;288:1635;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1098:25:32;288:1635:42;;;-1:-1:-1;;288:1635:42;;;1098:25:32;;;288:1635:42;1098:25:32;288:1635:42;1098:25:32;288:1635:42;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1605:87;288:1635;;;;1651:30;;;;;;288:1635;1651:30;;288:1635;1651:30;288:1635;;;;;;;;;;-1:-1:-1;;288:1635:42;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;288:1635:42;;;;;;;815:17;288:1635;;;;;;811:91;;288:1635;1003:3;288:1635;;974:27;;;;;1045:19;;;;;:::i;:::-;288:1635;;;;1026:18;288:1635;;;;;;1026:44;1022:128;;-1:-1:-1;;288:1635:42;;;;;;959:13;;288:1635;;;;;;;;;;;;1022:128;1115:19;;;;;;:::i;:::-;288:1635;;;-1:-1:-1;;;1097:38:42;;288:1635;;;1097:38;;288:1635;1097:38;974:27;;;;;288:1635;;;;;;;;;;;;;;;;;;;;;;1189:83;;288:1635;-1:-1:-1;;;;;;;;;;;288:1635:42;;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;288:1635:42;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;954:206;288:1635;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;288:1635:42;;;;;;;;1189:83;1344:39;288:1635;1189:83;;288:1635;1189:83;;;288:1635;;;;-1:-1:-1;;;;;;;;;;;288:1635:42;;;;;;815:17;288:1635;;;;;;;;;;;;;;1344:39;;288:1635;;;;;;;;;;;;-1:-1:-1;;;288:1635:42;;;;;1344:39;288:1635;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;288:1635:42;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;288:1635:42;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;288:1635:42;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;288:1635:42;;;;;;;;;-1:-1:-1;;;;288:1635:42;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;811:91;288:1635;;-1:-1:-1;;;864:27:42;;288:1635;;;864:27;;288:1635;-1:-1:-1;288:1635:42;-1:-1:-1;864:27:42;288:1635;;;-1:-1:-1;;;;;;288:1635:42;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;288:1635:42;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;288:1635:42;;;;;;;-1:-1:-1;288:1635:42;;;;;;-1:-1:-1;288:1635:42;:::o", - "linkReferences": {} - }, - "methodIdentifiers": { - "adjustStockPlanPool(bytes16,uint256)": "e1c5951c", - "createStockPlan(bytes16,bytes16[],uint256)": "6b348f41" - }, - "rawMetadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stock_class_id\",\"type\":\"bytes16\"}],\"name\":\"InvalidStockClass\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stock_plan_id\",\"type\":\"bytes16\"}],\"name\":\"StockPlanAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stock_plan_id\",\"type\":\"bytes16\"}],\"name\":\"StockPlanNotFound\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes16\",\"name\":\"id\",\"type\":\"bytes16\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"shares_reserved\",\"type\":\"uint256\"}],\"name\":\"StockPlanCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes16\",\"name\":\"id\",\"type\":\"bytes16\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newSharesReserved\",\"type\":\"uint256\"}],\"name\":\"StockPlanSharesReservedAdjusted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"enum TxType\",\"name\":\"txType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"txData\",\"type\":\"bytes\"}],\"name\":\"TxCreated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stockPlanId\",\"type\":\"bytes16\"},{\"internalType\":\"uint256\",\"name\":\"newSharesReserved\",\"type\":\"uint256\"}],\"name\":\"adjustStockPlanPool\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"_id\",\"type\":\"bytes16\"},{\"internalType\":\"bytes16[]\",\"name\":\"_stock_class_ids\",\"type\":\"bytes16[]\"},{\"internalType\":\"uint256\",\"name\":\"_shares_reserved\",\"type\":\"uint256\"}],\"name\":\"createStockPlan\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/lib/diamond/facets/StockPlanFacet.sol\":\"StockPlanFacet\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"appendCBOR\":false,\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@diamond/=src/lib/diamond/\",\":diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/\",\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\",\":openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/\"],\"viaIR\":true},\"sources\":{\"lib/diamond-3-hardhat/contracts/interfaces/IDiamondCut.sol\":{\"keccak256\":\"0xc00c16bfa30a3fa5f3dc684f7f8ba62c259962b25f647d9588739458989717fc\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://119d9a5acd99b9462a8341c9b95ddd468648799eefa47038f81521431743c1ae\",\"dweb:/ipfs/QmTF7WNyPWTUtUzNcpq5rf5v2uw5TwzqsSg9D53pfQufcu\"]},\"lib/diamond-3-hardhat/contracts/libraries/LibDiamond.sol\":{\"keccak256\":\"0xc5044f5a7a031e4e1869a26addf83b25c8b20d5949ba13b613dfbc72ad2f63b0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://25f2ac88867d97bfd13b503c06512b082ecf861f3f00702ca2747502c9113a79\",\"dweb:/ipfs/QmSYDNeaLGXDsPa2maoaeim5qJiLNuM3PbDguoYmsUmAZL\"]},\"src/lib/Structs.sol\":{\"keccak256\":\"0x404e740ae677baf5cc57884ee32d9accb367ae58f56a23f4e59b4f2987ae5100\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://349ec5d7e23684f71836001d70c012e59c0f2c94c9fa1fae83f85eeccda1fe52\",\"dweb:/ipfs/QmTVT5hzGePT8FxFxWhX77bH1DS1Z8iu8NAfAdn4PfsyCh\"]},\"src/lib/diamond/DiamondTxHelper.sol\":{\"keccak256\":\"0xe72ba093a5b16735d0193624d5b6585deb2cfe7e419a468c90fd70317fccf5e6\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://4ae3bd1ccbe005c63bed07e255c2b25c9bfdb37b9d85bb75f8f65cfeb0f6df98\",\"dweb:/ipfs/QmbTpzVeLkvthzpasQLvuvAfwE3g4sMsjEfpRBNwyG4eMQ\"]},\"src/lib/diamond/Storage.sol\":{\"keccak256\":\"0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d\",\"dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL\"]},\"src/lib/diamond/Structs.sol\":{\"keccak256\":\"0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab\",\"dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD\"]},\"src/lib/diamond/facets/StockPlanFacet.sol\":{\"keccak256\":\"0x2f7dae680ad926c2788ce52e65cdb95b31e323c1e04f4e69d304e68e81e4e727\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6d2de2814568867928c4340841625dbd2b0094bc224b1488f03f9799b949fe28\",\"dweb:/ipfs/QmU58fHpqy3Bp79ZoshMu6Rewp6PZTzrH8hmCERmy1u35o\"]}},\"version\":1}", - "metadata": { - "compiler": { - "version": "0.8.20+commit.a1b79de6" - }, - "language": "Solidity", - "output": { - "abi": [ - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stock_class_id", - "type": "bytes16" - } - ], - "type": "error", - "name": "InvalidStockClass" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stock_plan_id", - "type": "bytes16" - } - ], - "type": "error", - "name": "StockPlanAlreadyExists" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stock_plan_id", - "type": "bytes16" - } - ], - "type": "error", - "name": "StockPlanNotFound" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "id", - "type": "bytes16", - "indexed": true - }, - { - "internalType": "uint256", - "name": "shares_reserved", - "type": "uint256", - "indexed": false - } - ], - "type": "event", - "name": "StockPlanCreated", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "id", - "type": "bytes16", - "indexed": true - }, - { - "internalType": "uint256", - "name": "newSharesReserved", - "type": "uint256", - "indexed": false - } - ], - "type": "event", - "name": "StockPlanSharesReservedAdjusted", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "enum TxType", - "name": "txType", - "type": "uint8", - "indexed": false - }, - { - "internalType": "bytes", - "name": "txData", - "type": "bytes", - "indexed": false - } - ], - "type": "event", - "name": "TxCreated", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stockPlanId", - "type": "bytes16" - }, - { - "internalType": "uint256", - "name": "newSharesReserved", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "adjustStockPlanPool" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "_id", - "type": "bytes16" - }, - { - "internalType": "bytes16[]", - "name": "_stock_class_ids", - "type": "bytes16[]" - }, - { - "internalType": "uint256", - "name": "_shares_reserved", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "createStockPlan" - } - ], - "devdoc": { - "kind": "dev", - "methods": {}, - "version": 1 - }, - "userdoc": { - "kind": "user", - "methods": {}, - "version": 1 - } - }, - "settings": { - "remappings": [ - "@diamond/=src/lib/diamond/", - "diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/", - "ds-test/=lib/forge-std/lib/ds-test/src/", - "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", - "forge-std/=lib/forge-std/src/", - "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", - "openzeppelin-contracts/=lib/openzeppelin-contracts/", - "openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/" - ], - "optimizer": { - "enabled": true, - "runs": 200 - }, - "metadata": { - "bytecodeHash": "none", - "appendCBOR": false - }, - "compilationTarget": { - "src/lib/diamond/facets/StockPlanFacet.sol": "StockPlanFacet" - }, - "evmVersion": "paris", - "libraries": {}, - "viaIR": true - }, - "sources": { - "lib/diamond-3-hardhat/contracts/interfaces/IDiamondCut.sol": { - "keccak256": "0xc00c16bfa30a3fa5f3dc684f7f8ba62c259962b25f647d9588739458989717fc", - "urls": [ - "bzz-raw://119d9a5acd99b9462a8341c9b95ddd468648799eefa47038f81521431743c1ae", - "dweb:/ipfs/QmTF7WNyPWTUtUzNcpq5rf5v2uw5TwzqsSg9D53pfQufcu" - ], - "license": "MIT" - }, - "lib/diamond-3-hardhat/contracts/libraries/LibDiamond.sol": { - "keccak256": "0xc5044f5a7a031e4e1869a26addf83b25c8b20d5949ba13b613dfbc72ad2f63b0", - "urls": [ - "bzz-raw://25f2ac88867d97bfd13b503c06512b082ecf861f3f00702ca2747502c9113a79", - "dweb:/ipfs/QmSYDNeaLGXDsPa2maoaeim5qJiLNuM3PbDguoYmsUmAZL" - ], - "license": "MIT" - }, - "src/lib/Structs.sol": { - "keccak256": "0x404e740ae677baf5cc57884ee32d9accb367ae58f56a23f4e59b4f2987ae5100", - "urls": [ - "bzz-raw://349ec5d7e23684f71836001d70c012e59c0f2c94c9fa1fae83f85eeccda1fe52", - "dweb:/ipfs/QmTVT5hzGePT8FxFxWhX77bH1DS1Z8iu8NAfAdn4PfsyCh" - ], - "license": "MIT" - }, - "src/lib/diamond/DiamondTxHelper.sol": { - "keccak256": "0xe72ba093a5b16735d0193624d5b6585deb2cfe7e419a468c90fd70317fccf5e6", - "urls": [ - "bzz-raw://4ae3bd1ccbe005c63bed07e255c2b25c9bfdb37b9d85bb75f8f65cfeb0f6df98", - "dweb:/ipfs/QmbTpzVeLkvthzpasQLvuvAfwE3g4sMsjEfpRBNwyG4eMQ" - ], - "license": "MIT" - }, - "src/lib/diamond/Storage.sol": { - "keccak256": "0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f", - "urls": [ - "bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d", - "dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL" - ], - "license": "MIT" - }, - "src/lib/diamond/Structs.sol": { - "keccak256": "0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2", - "urls": [ - "bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab", - "dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/StockPlanFacet.sol": { - "keccak256": "0x2f7dae680ad926c2788ce52e65cdb95b31e323c1e04f4e69d304e68e81e4e727", - "urls": [ - "bzz-raw://6d2de2814568867928c4340841625dbd2b0094bc224b1488f03f9799b949fe28", - "dweb:/ipfs/QmU58fHpqy3Bp79ZoshMu6Rewp6PZTzrH8hmCERmy1u35o" - ], - "license": "MIT" - } - }, - "version": 1 - }, - "id": 42 -} \ No newline at end of file diff --git a/chain/out/WarrantFacet.sol/WarrantFacet.json b/chain/out/WarrantFacet.sol/WarrantFacet.json deleted file mode 100644 index f18ca0d8..00000000 --- a/chain/out/WarrantFacet.sol/WarrantFacet.json +++ /dev/null @@ -1,294 +0,0 @@ -{ - "abi": [ - { - "type": "function", - "name": "getWarrantPosition", - "inputs": [ - { - "name": "securityId", - "type": "bytes16", - "internalType": "bytes16" - } - ], - "outputs": [ - { - "name": "", - "type": "tuple", - "internalType": "struct WarrantActivePosition", - "components": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "quantity", - "type": "uint256", - "internalType": "uint256" - } - ] - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "issueWarrant", - "inputs": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - }, - { - "name": "quantity", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "security_id", - "type": "bytes16", - "internalType": "bytes16" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "event", - "name": "TxCreated", - "inputs": [ - { - "name": "txType", - "type": "uint8", - "indexed": false, - "internalType": "enum TxType" - }, - { - "name": "txData", - "type": "bytes", - "indexed": false, - "internalType": "bytes" - } - ], - "anonymous": false - }, - { - "type": "error", - "name": "InvalidQuantity", - "inputs": [] - }, - { - "type": "error", - "name": "NoStakeholder", - "inputs": [ - { - "name": "stakeholder_id", - "type": "bytes16", - "internalType": "bytes16" - } - ] - } - ], - "bytecode": { - "object": "0x60808060405234610016576103d6908161001c8239f35b600080fdfe608060408181526004908136101561001657600080fd5b60009260e0908435821c908163bcc25ca9146102f3575063e462db991461003c57600080fd5b346102ef5760603660031901126102ef57610055610388565b6001600160801b0319926044359291602490858516908235908287036102eb577f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfc805460001981146102d957600101905587811697888b526020977f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf78952868c2054156102c35783156102b357865160018a898f8e6100f3866103a4565b85527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c07838601938a85528b83525220925160801c8584541617835551910155898c527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c068952868c2090815491680100000000000000008310156102a1576001830180825583101561028f578d52898d208260011c01916001600160801b03608084549260071b169260801c831b921b1916179055838b527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c088852858b209160801c908254161790558351968688015283870152606086015260608552608085019585871067ffffffffffffffff88111761027e575050908185879352600d865260a08501528351928360c0860152825b848110610269578486018301849052837f88aa35e399ceb8d6b2e810abb3df607c140128e7c089c428f212598bda6a7eb7601f8701601f1916880188900360600189a180f35b85810180830151908401528793508101610223565b634e487b7160e01b88526041905286fd5b634e487b7160e01b8e5260328d52878efd5b634e487b7160e01b8e5260418d52878efd5b865163524f409b60e01b81528b90fd5b865163b4586dfb60e01b8152808c018b90528690fd5b634e487b7160e01b8c5260118b52858cfd5b8980fd5b8380fd5b83908634610385576020366003190112610385578083916020610314610388565b9461031e816103a4565b82815201526001600160801b031980931681527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c07602052209060206001845192610367846103a4565b845460801b1693848452015491019081528251918252516020820152f35b80fd5b600435906001600160801b03198216820361039f57565b600080fd5b6040810190811067ffffffffffffffff8211176103c057604052565b634e487b7160e01b600052604160045260246000fd", - "sourceMap": "291:1146:76:-:0;;;;;;;;;;;;;;;;;", - "linkReferences": {} - }, - "deployedBytecode": { - "object": "0x608060408181526004908136101561001657600080fd5b60009260e0908435821c908163bcc25ca9146102f3575063e462db991461003c57600080fd5b346102ef5760603660031901126102ef57610055610388565b6001600160801b0319926044359291602490858516908235908287036102eb577f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bfc805460001981146102d957600101905587811697888b526020977f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26bf78952868c2054156102c35783156102b357865160018a898f8e6100f3866103a4565b85527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c07838601938a85528b83525220925160801c8584541617835551910155898c527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c068952868c2090815491680100000000000000008310156102a1576001830180825583101561028f578d52898d208260011c01916001600160801b03608084549260071b169260801c831b921b1916179055838b527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c088852858b209160801c908254161790558351968688015283870152606086015260608552608085019585871067ffffffffffffffff88111761027e575050908185879352600d865260a08501528351928360c0860152825b848110610269578486018301849052837f88aa35e399ceb8d6b2e810abb3df607c140128e7c089c428f212598bda6a7eb7601f8701601f1916880188900360600189a180f35b85810180830151908401528793508101610223565b634e487b7160e01b88526041905286fd5b634e487b7160e01b8e5260328d52878efd5b634e487b7160e01b8e5260418d52878efd5b865163524f409b60e01b81528b90fd5b865163b4586dfb60e01b8152808c018b90528690fd5b634e487b7160e01b8c5260118b52858cfd5b8980fd5b8380fd5b83908634610385576020366003190112610385578083916020610314610388565b9461031e816103a4565b82815201526001600160801b031980931681527f2031468f0c30f7087de4da9398818763b546d7f89935fa65485c24ff1df26c07602052209060206001845192610367846103a4565b845460801b1693848452015491019081528251918252516020820152f35b80fd5b600435906001600160801b03198216820361039f57565b600080fd5b6040810190811067ffffffffffffffff8211176103c057604052565b634e487b7160e01b600052604160045260246000fd", - "sourceMap": "291:1146:76:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;291:1146:76;;;;;;:::i;:::-;-1:-1:-1;;;;;;291:1146:76;;;;;;;;;;;;;;;;;;;470:8;291:1146;;-1:-1:-1;;291:1146:76;;;;;;;;;;;;;;;;;686:19:77;291:1146:76;;;;;;686:40:77;682:107;;1357:13;;1353:43;;291:1146:76;;;;;;;;;;:::i;:::-;;;638:36;690:77;;;291:1146;;;;;;;;;;;;;;;;;;;;;;;;;;;638:25;291:1146;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;291:1146:76;;;;;;;;;;;;;;;;;;;;;;954:47;291:1146;;;;;;;;;;;;;;;;;1093:49;;;;291:1146;;;;;;;;;;1093:49;;291:1146;;;;;;;;;;;;;;;;;;;;;1170:23;291:1146;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1098:25:65;291:1146:76;;;-1:-1:-1;;291:1146:76;;;1098:25:65;;;291:1146:76;1098:25:65;291:1146:76;1098:25:65;291:1146:76;;;;;;;;;;;;;;;;-1:-1:-1;291:1146:76;;;;;-1:-1:-1;;;291:1146:76;;;;;;;;-1:-1:-1;;;291:1146:76;;;;;;;;;-1:-1:-1;;;291:1146:76;;;;;;;;1353:43:77;291:1146:76;;-1:-1:-1;;;1379:17:77;;291:1146:76;;1379:17:77;682:107;291:1146:76;;-1:-1:-1;;;749:29:77;;;;;291:1146:76;;;;;749:29:77;291:1146:76;-1:-1:-1;;;291:1146:76;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;291:1146:76;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;291:1146:76;;;;;;1380:36;291:1146;;;;;1380:36;291:1146;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;291:1146:76;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;", - "linkReferences": {} - }, - "methodIdentifiers": { - "getWarrantPosition(bytes16)": "bcc25ca9", - "issueWarrant(bytes16,uint256,bytes16)": "e462db99" - }, - "rawMetadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"InvalidQuantity\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"}],\"name\":\"NoStakeholder\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"enum TxType\",\"name\":\"txType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"txData\",\"type\":\"bytes\"}],\"name\":\"TxCreated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"securityId\",\"type\":\"bytes16\"}],\"name\":\"getWarrantPosition\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"}],\"internalType\":\"struct WarrantActivePosition\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"stakeholder_id\",\"type\":\"bytes16\"},{\"internalType\":\"uint256\",\"name\":\"quantity\",\"type\":\"uint256\"},{\"internalType\":\"bytes16\",\"name\":\"security_id\",\"type\":\"bytes16\"}],\"name\":\"issueWarrant\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/lib/diamond/facets/WarrantFacet.sol\":\"WarrantFacet\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"appendCBOR\":false,\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@diamond/=src/lib/diamond/\",\":diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/\",\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\",\":openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/\"],\"viaIR\":true},\"sources\":{\"src/lib/Structs.sol\":{\"keccak256\":\"0x404e740ae677baf5cc57884ee32d9accb367ae58f56a23f4e59b4f2987ae5100\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://349ec5d7e23684f71836001d70c012e59c0f2c94c9fa1fae83f85eeccda1fe52\",\"dweb:/ipfs/QmTVT5hzGePT8FxFxWhX77bH1DS1Z8iu8NAfAdn4PfsyCh\"]},\"src/lib/diamond/DiamondTxHelper.sol\":{\"keccak256\":\"0xe72ba093a5b16735d0193624d5b6585deb2cfe7e419a468c90fd70317fccf5e6\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://4ae3bd1ccbe005c63bed07e255c2b25c9bfdb37b9d85bb75f8f65cfeb0f6df98\",\"dweb:/ipfs/QmbTpzVeLkvthzpasQLvuvAfwE3g4sMsjEfpRBNwyG4eMQ\"]},\"src/lib/diamond/Storage.sol\":{\"keccak256\":\"0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d\",\"dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL\"]},\"src/lib/diamond/Structs.sol\":{\"keccak256\":\"0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab\",\"dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD\"]},\"src/lib/diamond/facets/WarrantFacet.sol\":{\"keccak256\":\"0xf31df59fc2568e1b76c2babcfc2621a425cc83090e7ed0daa773ef4a7bac8773\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://b3403064921f8c7ef4cd3b8eb3bf58587bc8c78da0237f58e90081da406080c4\",\"dweb:/ipfs/QmYPud1rJR5jzhY19cPor2oPtVm3B3JybbbQBmtxXpjwmw\"]},\"src/lib/diamond/libraries/ValidationLib.sol\":{\"keccak256\":\"0xfcd772b0670acded810468d772729715e3cd4db25c02a0da77c7445903e1cc5f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d572672df16392825ed86c32a53453247f62b481787db0498993f857623e1ee6\",\"dweb:/ipfs/Qmbpoa5EZ7yRcB1mZkmb5nhfxhhFY9g8tMJxPf9ZrzfNfL\"]}},\"version\":1}", - "metadata": { - "compiler": { - "version": "0.8.20+commit.a1b79de6" - }, - "language": "Solidity", - "output": { - "abi": [ - { - "inputs": [], - "type": "error", - "name": "InvalidQuantity" - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - } - ], - "type": "error", - "name": "NoStakeholder" - }, - { - "inputs": [ - { - "internalType": "enum TxType", - "name": "txType", - "type": "uint8", - "indexed": false - }, - { - "internalType": "bytes", - "name": "txData", - "type": "bytes", - "indexed": false - } - ], - "type": "event", - "name": "TxCreated", - "anonymous": false - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "securityId", - "type": "bytes16" - } - ], - "stateMutability": "view", - "type": "function", - "name": "getWarrantPosition", - "outputs": [ - { - "internalType": "struct WarrantActivePosition", - "name": "", - "type": "tuple", - "components": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - }, - { - "internalType": "uint256", - "name": "quantity", - "type": "uint256" - } - ] - } - ] - }, - { - "inputs": [ - { - "internalType": "bytes16", - "name": "stakeholder_id", - "type": "bytes16" - }, - { - "internalType": "uint256", - "name": "quantity", - "type": "uint256" - }, - { - "internalType": "bytes16", - "name": "security_id", - "type": "bytes16" - } - ], - "stateMutability": "nonpayable", - "type": "function", - "name": "issueWarrant" - } - ], - "devdoc": { - "kind": "dev", - "methods": {}, - "version": 1 - }, - "userdoc": { - "kind": "user", - "methods": {}, - "version": 1 - } - }, - "settings": { - "remappings": [ - "@diamond/=src/lib/diamond/", - "diamond-3-hardhat/=lib/diamond-3-hardhat/contracts/", - "ds-test/=lib/forge-std/lib/ds-test/src/", - "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", - "forge-std/=lib/forge-std/src/", - "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", - "openzeppelin-contracts/=lib/openzeppelin-contracts/", - "openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/" - ], - "optimizer": { - "enabled": true, - "runs": 200 - }, - "metadata": { - "bytecodeHash": "none", - "appendCBOR": false - }, - "compilationTarget": { - "src/lib/diamond/facets/WarrantFacet.sol": "WarrantFacet" - }, - "evmVersion": "paris", - "libraries": {}, - "viaIR": true - }, - "sources": { - "src/lib/Structs.sol": { - "keccak256": "0x404e740ae677baf5cc57884ee32d9accb367ae58f56a23f4e59b4f2987ae5100", - "urls": [ - "bzz-raw://349ec5d7e23684f71836001d70c012e59c0f2c94c9fa1fae83f85eeccda1fe52", - "dweb:/ipfs/QmTVT5hzGePT8FxFxWhX77bH1DS1Z8iu8NAfAdn4PfsyCh" - ], - "license": "MIT" - }, - "src/lib/diamond/DiamondTxHelper.sol": { - "keccak256": "0xe72ba093a5b16735d0193624d5b6585deb2cfe7e419a468c90fd70317fccf5e6", - "urls": [ - "bzz-raw://4ae3bd1ccbe005c63bed07e255c2b25c9bfdb37b9d85bb75f8f65cfeb0f6df98", - "dweb:/ipfs/QmbTpzVeLkvthzpasQLvuvAfwE3g4sMsjEfpRBNwyG4eMQ" - ], - "license": "MIT" - }, - "src/lib/diamond/Storage.sol": { - "keccak256": "0x5534e8d79be2bb31bdc004982e95a3e6e4e515312ec0ca2cf38a4a50c7a62e2f", - "urls": [ - "bzz-raw://1c42f04102bfd02153434b69c986db79baad9bdc96e791f57ade9a096527843d", - "dweb:/ipfs/Qmb8bUxXJqbPQjNhGcfcGgZEr2GKfhYWxJEJvf87CX9MbL" - ], - "license": "MIT" - }, - "src/lib/diamond/Structs.sol": { - "keccak256": "0x111f5e7a7b9a71745925edc3019c17afcfaf21848dadde9163bbab8b0fdf3bc2", - "urls": [ - "bzz-raw://68b545403c8460e2971b220d1b462229566f28313f97ea129085f57ac4176dab", - "dweb:/ipfs/QmXeuWr1crQXhrDVcFKVF33XpTaK3pY1oKxugQKwXX4XuD" - ], - "license": "MIT" - }, - "src/lib/diamond/facets/WarrantFacet.sol": { - "keccak256": "0xf31df59fc2568e1b76c2babcfc2621a425cc83090e7ed0daa773ef4a7bac8773", - "urls": [ - "bzz-raw://b3403064921f8c7ef4cd3b8eb3bf58587bc8c78da0237f58e90081da406080c4", - "dweb:/ipfs/QmYPud1rJR5jzhY19cPor2oPtVm3B3JybbbQBmtxXpjwmw" - ], - "license": "MIT" - }, - "src/lib/diamond/libraries/ValidationLib.sol": { - "keccak256": "0xfcd772b0670acded810468d772729715e3cd4db25c02a0da77c7445903e1cc5f", - "urls": [ - "bzz-raw://d572672df16392825ed86c32a53453247f62b481787db0498993f857623e1ee6", - "dweb:/ipfs/Qmbpoa5EZ7yRcB1mZkmb5nhfxhhFY9g8tMJxPf9ZrzfNfL" - ], - "license": "MIT" - } - }, - "version": 1 - }, - "id": 76 -} \ No newline at end of file diff --git a/chain/remappings.txt b/chain/remappings.txt index 13c30515..a761fffd 100644 --- a/chain/remappings.txt +++ b/chain/remappings.txt @@ -8,3 +8,4 @@ openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/ @facets/=src/facets/ @libraries/=src/libraries/ @core/=src/core/ +@interfaces/=src/interfaces/ diff --git a/chain/script/AcceptAdminTransfers.s.sol b/chain/script/AcceptAdminTransfers.s.sol new file mode 100644 index 00000000..55eaf0ba --- /dev/null +++ b/chain/script/AcceptAdminTransfers.s.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "forge-std/Script.sol"; +import "forge-std/console.sol"; +import "../src/core/CapTableFactory.sol"; +import { AccessControlFacet } from "@facets/AccessControlFacet.sol"; +import { AccessControl } from "@libraries/AccessControl.sol"; + +contract AcceptAdminTransfersScript is Script { + function run() external { + // Get the fairmint wallet private key + uint256 fairmintPrivateKey = vm.envUint("PRIVATE_KEY"); + address fairmintWallet = vm.addr(fairmintPrivateKey); + if (fairmintWallet == address(0)) { + revert("Invalid private key"); + } + + // Get factory address from env + address factoryAddress = vm.envAddress("FACTORY_ADDRESS"); + CapTableFactory factory = CapTableFactory(factoryAddress); + + console.log("Starting admin transfer acceptance for Fairmint wallet:", fairmintWallet); + console.log("Factory address:", factoryAddress); + + vm.startBroadcast(fairmintPrivateKey); + + uint256 capTableCount = factory.getCapTableCount(); + console.log("Total cap tables:", capTableCount); + + for (uint256 i = 0; i < capTableCount; i++) { + address capTable = factory.capTables(i); + console.log("\nChecking cap table:", capTable); + + // Check if we're already admin + if (AccessControlFacet(capTable).hasRole(AccessControl.DEFAULT_ADMIN_ROLE, fairmintWallet)) { + console.log("Already admin, skipping..."); + continue; + } + + // Check if we're the pending admin + if (AccessControlFacet(capTable).getPendingAdmin() == fairmintWallet) { + console.log("Accepting admin transfer..."); + AccessControlFacet(capTable).acceptAdmin(); + console.log("Admin transfer accepted!"); + } else { + console.log("Not pending admin, skipping..."); + } + } + + vm.stopBroadcast(); + console.log("\nAdmin transfer acceptance complete!"); + } +} diff --git a/chain/script/CapTableFactory.s.sol b/chain/script/CapTableFactory.s.sol deleted file mode 100644 index 29028492..00000000 --- a/chain/script/CapTableFactory.s.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "forge-std/Script.sol"; -import "forge-std/console.sol"; - -import "../src/CapTable.sol"; -import "../src/CapTableFactory.sol"; - -/// @dev Test deployment using `forge script script/CapTableFactory.s.sol --fork-url http://localhost:8545 --broadcast` -contract DeployCapTableFactoryDeployLocalScript is Script { - uint256 deployerPrivateKey; - - function setUp() public { - console.log("Upgrading CapTableFactory with CapTable implementation"); - - deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - } - - function run() external { - console.log("Deploying CapTableFactory and CapTable implementation"); - - vm.startBroadcast(deployerPrivateKey); // Start a new transaction - - CapTable capTable = new CapTable(); - console.log("CapTable implementation deployed at:", address(capTable)); - - vm.stopBroadcast(); // End the transaction - - // Deploy CapTableFactory with the address of CapTable implementation - vm.startBroadcast(deployerPrivateKey); // Start a new transaction - - CapTableFactory capTableFactory = new CapTableFactory(address(capTable)); - console.log("CapTableFactory deployed at:", address(capTableFactory)); - - vm.stopBroadcast(); // End the transaction - } - - /// @dev Run using `forge tx script/CapTableFactory.s.sol upgradeCapTable [0x...] --fork-url http://localhost:8545 --broadcast` - function upgradeCapTable(address factory) external { - vm.startBroadcast(deployerPrivateKey); // Start a new transaction - - CapTable capTable = new CapTable(); - console.log("CapTable implementation deployed at:", address(capTable)); - - vm.stopBroadcast(); // End the transaction - - // Upgrade CapTableFactory with the address of CapTable implementation - vm.startBroadcast(deployerPrivateKey); // Start a new transaction - - CapTableFactory capTableFactory = CapTableFactory(factory); - - capTableFactory.updateCapTableImplementation(address(capTable)); - console.log("CapTableFactory upgraded to:", address(capTable)); - - vm.stopBroadcast(); // End the transaction - } -} diff --git a/chain/script/DeployCapTable.s.sol b/chain/script/DeployCapTable.s.sol deleted file mode 100644 index d9849895..00000000 --- a/chain/script/DeployCapTable.s.sol +++ /dev/null @@ -1,158 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "forge-std/Script.sol"; -import "forge-std/console.sol"; -import "../src/core/CapTableFactory.sol"; -import { DiamondCutFacet } from "diamond-3-hardhat/facets/DiamondCutFacet.sol"; -import { IssuerFacet } from "@facets/IssuerFacet.sol"; -import { StakeholderFacet } from "@facets/StakeholderFacet.sol"; -import { StockClassFacet } from "@facets/StockClassFacet.sol"; -import { StockFacet } from "@facets/StockFacet.sol"; -import { ConvertiblesFacet } from "@facets/ConvertiblesFacet.sol"; -import { EquityCompensationFacet } from "@facets/EquityCompensationFacet.sol"; -import { StockPlanFacet } from "@facets/StockPlanFacet.sol"; -import { WarrantFacet } from "@facets/WarrantFacet.sol"; -import { StakeholderNFTFacet } from "@facets/StakeholderNFTFacet.sol"; - -contract DeployDiamondCapTableScript is Script { - function setUp() public { - // Setup for Base Sepolia deployment - } - function checkEnv( - address diamondCutFacet, - address issuerFacet, - address stakeholderFacet, - address stockClassFacet, - address stockFacet, - address convertiblesFacet, - address equityCompensationFacet, - address stockPlanFacet, - address warrantFacet, - address stakeholderNFTFacet - ) public view returns (bool) { - // check one by one - if (diamondCutFacet == address(0)) { - console.log("DIAMOND_CUT_FACET not set"); - return false; - } - if (issuerFacet == address(0)) { - console.log("ISSUER_FACET not set"); - return false; - } - if (stakeholderFacet == address(0)) { - console.log("STAKEHOLDER_FACET not set"); - return false; - } - if (stockClassFacet == address(0)) { - console.log("STOCK_CLASS_FACET not set"); - return false; - } - if (stockFacet == address(0)) { - console.log("STOCK_FACET not set"); - return false; - } - if (convertiblesFacet == address(0)) { - console.log("CONVERTIBLES_FACET not set"); - return false; - } - if (equityCompensationFacet == address(0)) { - console.log("EQUITY_COMPENSATION_FACET not set"); - return false; - } - if (stockPlanFacet == address(0)) { - console.log("STOCK_PLAN_FACET not set"); - return false; - } - if (warrantFacet == address(0)) { - console.log("WARRANT_FACET not set"); - return false; - } - if (stakeholderNFTFacet == address(0)) { - console.log("STAKEHOLDER_NFT_FACET not set"); - return false; - } - return true; - } - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - console.log("Deploying DiamondCapTable system to Base Sepolia"); - - vm.startBroadcast(deployerPrivateKey); - - // Try to get addresses from env - address diamondCutFacet = vm.envOr("DIAMOND_CUT_FACET", address(0)); - address issuerFacet = vm.envOr("ISSUER_FACET", address(0)); - address stakeholderFacet = vm.envOr("STAKEHOLDER_FACET", address(0)); - address stockClassFacet = vm.envOr("STOCK_CLASS_FACET", address(0)); - address stockFacet = vm.envOr("STOCK_FACET", address(0)); - address convertiblesFacet = vm.envOr("CONVERTIBLES_FACET", address(0)); - address equityCompensationFacet = vm.envOr("EQUITY_COMPENSATION_FACET", address(0)); - address stockPlanFacet = vm.envOr("STOCK_PLAN_FACET", address(0)); - address warrantFacet = vm.envOr("WARRANT_FACET", address(0)); - address stakeholderNFTFacet = vm.envOr("STAKEHOLDER_NFT_FACET", address(0)); - - bool allSet = checkEnv( - diamondCutFacet, - issuerFacet, - stakeholderFacet, - stockClassFacet, - stockFacet, - convertiblesFacet, - equityCompensationFacet, - stockPlanFacet, - warrantFacet, - stakeholderNFTFacet - ); - - // Deploy new facets if addresses not in env - if (!allSet) { - revert("One or more required addresses are not set in the .env file"); - // console.log("Deploying new facets..."); - // diamondCutFacet = address(new DiamondCutFacet()); - // issuerFacet = address(new IssuerFacet()); - // stakeholderFacet = address(new StakeholderFacet()); - // stockClassFacet = address(new StockClassFacet()); - // stockFacet = address(new StockFacet()); - // convertiblesFacet = address(new ConvertiblesFacet()); - // equityCompensationFacet = address(new EquityCompensationFacet()); - // stockPlanFacet = address(new StockPlanFacet()); - // warrantFacet = address(new WarrantFacet()); - // stakeholderNFTFacet = address(new StakeholderNFTFacet()); - - console.log("------- New Facet Addresses (Add to .env) -------"); - console.log("DIAMOND_CUT_FACET=", diamondCutFacet); - console.log("ISSUER_FACET=", issuerFacet); - console.log("STAKEHOLDER_FACET=", stakeholderFacet); - console.log("STOCK_CLASS_FACET=", stockClassFacet); - console.log("STOCK_FACET=", stockFacet); - console.log("CONVERTIBLES_FACET=", convertiblesFacet); - console.log("EQUITY_COMPENSATION_FACET=", equityCompensationFacet); - console.log("STOCK_PLAN_FACET=", stockPlanFacet); - console.log("WARRANT_FACET=", warrantFacet); - console.log("STAKEHOLDER_NFT_FACET=", stakeholderNFTFacet); - console.log("-------------------------------------------------"); - } else { - console.log("Using existing facets from .env"); - } - - // Deploy factory with facet addresses - CapTableFactory factory = new CapTableFactory( - diamondCutFacet, - issuerFacet, - stakeholderFacet, - stockClassFacet, - stockFacet, - convertiblesFacet, - equityCompensationFacet, - stockPlanFacet, - warrantFacet, - stakeholderNFTFacet - ); - - console.log("\nDiamondCapTableFactory deployed at:", address(factory)); - - vm.stopBroadcast(); - } -} diff --git a/chain/script/DeployFactory.s.sol b/chain/script/DeployFactory.s.sol new file mode 100644 index 00000000..bb16d40d --- /dev/null +++ b/chain/script/DeployFactory.s.sol @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "forge-std/Script.sol"; +import "forge-std/console.sol"; +import { MockFacet, MockFacetV2 } from "../test/mocks/MockFacet.sol"; +import { CapTableFactory } from "@core/CapTableFactory.sol"; +import { CapTable } from "@core/CapTable.sol"; +import { DiamondCutFacet } from "diamond-3-hardhat/facets/DiamondCutFacet.sol"; +import { IssuerFacet } from "@facets/IssuerFacet.sol"; +import { DiamondLoupeFacet } from "diamond-3-hardhat/facets/DiamondLoupeFacet.sol"; +import { IDiamondCut } from "diamond-3-hardhat/interfaces/IDiamondCut.sol"; +import { StakeholderFacet } from "@facets/StakeholderFacet.sol"; +import { StockClassFacet } from "@facets/StockClassFacet.sol"; +import { StockFacet } from "@facets/StockFacet.sol"; +import { ConvertiblesFacet } from "@facets/ConvertiblesFacet.sol"; +import { EquityCompensationFacet } from "@facets/EquityCompensationFacet.sol"; +import { StockPlanFacet } from "@facets/StockPlanFacet.sol"; +import { WarrantFacet } from "@facets/WarrantFacet.sol"; +import { StakeholderNFTFacet } from "@facets/StakeholderNFTFacet.sol"; +import { AccessControl } from "@libraries/AccessControl.sol"; +import { AccessControlFacet } from "@facets/AccessControlFacet.sol"; + +library LibDeployment { + uint256 constant FACET_COUNT = 11; // Number of enum values FacetType + + enum FacetType { + DiamondLoupe, + Issuer, + Stakeholder, + StockClass, + Stock, + Convertibles, + EquityCompensation, + StockPlan, + Warrant, + StakeholderNFT, + AccessControl, + MockFacet, + MockFacetV2 + } + + struct FacetCutInfo { + string name; + bytes4[] selectors; + } + + function getFacetCutInfo(FacetType facetType) internal pure returns (FacetCutInfo memory info) { + if (facetType == FacetType.DiamondLoupe) { + bytes4[] memory selectors = new bytes4[](5); + selectors[0] = DiamondLoupeFacet.facets.selector; + selectors[1] = DiamondLoupeFacet.facetFunctionSelectors.selector; + selectors[2] = DiamondLoupeFacet.facetAddresses.selector; + selectors[3] = DiamondLoupeFacet.facetAddress.selector; + selectors[4] = DiamondLoupeFacet.supportsInterface.selector; + return FacetCutInfo({ name: "DiamondLoupeFacet", selectors: selectors }); + } + if (facetType == FacetType.Issuer) { + bytes4[] memory selectors = new bytes4[](2); + selectors[0] = IssuerFacet.initializeIssuer.selector; + selectors[1] = IssuerFacet.adjustIssuerAuthorizedShares.selector; + return FacetCutInfo({ name: "IssuerFacet", selectors: selectors }); + } + if (facetType == FacetType.Stakeholder) { + bytes4[] memory selectors = new bytes4[](3); + selectors[0] = StakeholderFacet.createStakeholder.selector; + selectors[1] = StakeholderFacet.getStakeholderPositions.selector; + selectors[2] = StakeholderFacet.linkStakeholderAddress.selector; + return FacetCutInfo({ name: "StakeholderFacet", selectors: selectors }); + } + if (facetType == FacetType.StockClass) { + bytes4[] memory selectors = new bytes4[](2); + selectors[0] = StockClassFacet.createStockClass.selector; + selectors[1] = StockClassFacet.adjustAuthorizedShares.selector; + return FacetCutInfo({ name: "StockClassFacet", selectors: selectors }); + } + if (facetType == FacetType.Stock) { + bytes4[] memory selectors = new bytes4[](4); + selectors[0] = StockFacet.issueStock.selector; + selectors[1] = StockFacet.getStockPosition.selector; + selectors[2] = StockFacet.transferStock.selector; + selectors[3] = StockFacet.getStakeholderSecurities.selector; + return FacetCutInfo({ name: "StockFacet", selectors: selectors }); + } + if (facetType == FacetType.Convertibles) { + bytes4[] memory selectors = new bytes4[](2); + selectors[0] = ConvertiblesFacet.issueConvertible.selector; + selectors[1] = ConvertiblesFacet.getConvertiblePosition.selector; + return FacetCutInfo({ name: "ConvertiblesFacet", selectors: selectors }); + } + if (facetType == FacetType.EquityCompensation) { + bytes4[] memory selectors = new bytes4[](3); + selectors[0] = EquityCompensationFacet.issueEquityCompensation.selector; + selectors[1] = EquityCompensationFacet.getPosition.selector; + selectors[2] = EquityCompensationFacet.exerciseEquityCompensation.selector; + return FacetCutInfo({ name: "EquityCompensationFacet", selectors: selectors }); + } + if (facetType == FacetType.StockPlan) { + bytes4[] memory selectors = new bytes4[](2); + selectors[0] = StockPlanFacet.createStockPlan.selector; + selectors[1] = StockPlanFacet.adjustStockPlanPool.selector; + return FacetCutInfo({ name: "StockPlanFacet", selectors: selectors }); + } + if (facetType == FacetType.Warrant) { + bytes4[] memory selectors = new bytes4[](2); + selectors[0] = WarrantFacet.issueWarrant.selector; + selectors[1] = WarrantFacet.getWarrantPosition.selector; + return FacetCutInfo({ name: "WarrantFacet", selectors: selectors }); + } + if (facetType == FacetType.StakeholderNFT) { + bytes4[] memory selectors = new bytes4[](2); + selectors[0] = StakeholderNFTFacet.mint.selector; + selectors[1] = StakeholderNFTFacet.tokenURI.selector; + return FacetCutInfo({ name: "StakeholderNFTFacet", selectors: selectors }); + } + if (facetType == FacetType.AccessControl) { + bytes4[] memory selectors = new bytes4[](8); + selectors[0] = AccessControlFacet.grantRole.selector; + selectors[1] = AccessControlFacet.revokeRole.selector; + selectors[2] = AccessControlFacet.hasRole.selector; + selectors[3] = AccessControlFacet.initializeAccessControl.selector; + selectors[4] = AccessControlFacet.transferAdmin.selector; + selectors[5] = AccessControlFacet.acceptAdmin.selector; + selectors[6] = AccessControlFacet.getAdmin.selector; + selectors[7] = AccessControlFacet.getPendingAdmin.selector; + return FacetCutInfo({ name: "AccessControlFacet", selectors: selectors }); + } + if (facetType == FacetType.MockFacet) { + bytes4[] memory selectors = new bytes4[](1); + selectors[0] = MockFacet.getValuePlusOne.selector; + return FacetCutInfo({ name: "MockFacet", selectors: selectors }); + } + if (facetType == FacetType.MockFacetV2) { + bytes4[] memory selectors = new bytes4[](1); + selectors[0] = MockFacetV2.getValuePlusTwo.selector; + return FacetCutInfo({ name: "MockFacetV2", selectors: selectors }); + } + revert("Unknown facet type"); + } + + function getFacetTypeFromSelector(bytes4 selector) internal pure returns (FacetType) { + if (selector == DiamondLoupeFacet.facets.selector) return FacetType.DiamondLoupe; + if (selector == IssuerFacet.initializeIssuer.selector) return FacetType.Issuer; + if (selector == StakeholderFacet.createStakeholder.selector) return FacetType.Stakeholder; + if (selector == StockClassFacet.createStockClass.selector) return FacetType.StockClass; + if (selector == StockFacet.issueStock.selector) return FacetType.Stock; + if (selector == ConvertiblesFacet.issueConvertible.selector) return FacetType.Convertibles; + if (selector == EquityCompensationFacet.issueEquityCompensation.selector) return FacetType.EquityCompensation; + if (selector == StockPlanFacet.createStockPlan.selector) return FacetType.StockPlan; + if (selector == WarrantFacet.issueWarrant.selector) return FacetType.Warrant; + if (selector == StakeholderNFTFacet.mint.selector) return FacetType.StakeholderNFT; + if (selector == AccessControlFacet.grantRole.selector) return FacetType.AccessControl; + if (selector == MockFacet.getValuePlusOne.selector) return FacetType.MockFacet; + if (selector == MockFacetV2.getValuePlusTwo.selector) return FacetType.MockFacetV2; + revert("Unknown selector"); + } + + function deployFacet(FacetType facetType) internal returns (address) { + address facetAddress; + if (facetType == FacetType.DiamondLoupe) { + facetAddress = address(new DiamondLoupeFacet()); + console.log("DIAMOND_LOUPE_FACET=", facetAddress); + return facetAddress; + } + if (facetType == FacetType.Issuer) { + facetAddress = address(new IssuerFacet()); + console.log("ISSUER_FACET=", facetAddress); + return facetAddress; + } + if (facetType == FacetType.Stakeholder) { + facetAddress = address(new StakeholderFacet()); + console.log("STAKEHOLDER_FACET=", facetAddress); + return facetAddress; + } + if (facetType == FacetType.StockClass) { + facetAddress = address(new StockClassFacet()); + console.log("STOCK_CLASS_FACET=", facetAddress); + return facetAddress; + } + if (facetType == FacetType.Stock) { + facetAddress = address(new StockFacet()); + console.log("STOCK_FACET=", facetAddress); + return facetAddress; + } + if (facetType == FacetType.Convertibles) { + facetAddress = address(new ConvertiblesFacet()); + console.log("CONVERTIBLES_FACET=", facetAddress); + return facetAddress; + } + if (facetType == FacetType.EquityCompensation) { + facetAddress = address(new EquityCompensationFacet()); + console.log("EQUITY_COMPENSATION_FACET=", facetAddress); + return facetAddress; + } + if (facetType == FacetType.StockPlan) { + facetAddress = address(new StockPlanFacet()); + console.log("STOCK_PLAN_FACET=", facetAddress); + return facetAddress; + } + if (facetType == FacetType.Warrant) { + facetAddress = address(new WarrantFacet()); + console.log("WARRANT_FACET=", facetAddress); + return facetAddress; + } + if (facetType == FacetType.StakeholderNFT) { + facetAddress = address(new StakeholderNFTFacet()); + console.log("STAKEHOLDER_NFT_FACET=", facetAddress); + return facetAddress; + } + if (facetType == FacetType.AccessControl) { + facetAddress = address(new AccessControlFacet()); + console.log("ACCESS_CONTROL_FACET=", facetAddress); + return facetAddress; + } + if (facetType == FacetType.MockFacet) { + facetAddress = address(new MockFacet()); + console.log("MOCK_FACET=", facetAddress); + return facetAddress; + } + if (facetType == FacetType.MockFacetV2) { + facetAddress = address(new MockFacetV2()); + console.log("MOCK_FACET_V2=", facetAddress); + return facetAddress; + } + revert("Unknown facet type"); + } + + function deployInitialFacets(address owner) internal returns (address) { + console.log("\n\nDeploying facets..."); + console.log("owner(this): ", address(this)); + + // Deploy all facets + IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](FACET_COUNT); // Change from FACET_COUNT to actual number of cuts + + // ------------------- Diamond Loupe Facet ------------------- + cuts[0] = IDiamondCut.FacetCut({ + facetAddress: LibDeployment.deployFacet(FacetType.DiamondLoupe), + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: LibDeployment.getFacetCutInfo(FacetType.DiamondLoupe).selectors + }); + + // ------------------- Issuer Facet ------------------- + cuts[1] = IDiamondCut.FacetCut({ + facetAddress: LibDeployment.deployFacet(FacetType.Issuer), + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: LibDeployment.getFacetCutInfo(FacetType.Issuer).selectors + }); + + // ------------------- Stakeholder Facet ------------------- + cuts[2] = IDiamondCut.FacetCut({ + facetAddress: LibDeployment.deployFacet(FacetType.Stakeholder), + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: LibDeployment.getFacetCutInfo(FacetType.Stakeholder).selectors + }); + + // ------------------- Stock Class Facet ------------------- + cuts[3] = IDiamondCut.FacetCut({ + facetAddress: LibDeployment.deployFacet(FacetType.StockClass), + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: LibDeployment.getFacetCutInfo(FacetType.StockClass).selectors + }); + + // ------------------- Stock Facet ------------------- + cuts[4] = IDiamondCut.FacetCut({ + facetAddress: LibDeployment.deployFacet(FacetType.Stock), + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: LibDeployment.getFacetCutInfo(FacetType.Stock).selectors + }); + + // ------------------- Convertibles Facet ------------------- + cuts[5] = IDiamondCut.FacetCut({ + facetAddress: LibDeployment.deployFacet(FacetType.Convertibles), + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: LibDeployment.getFacetCutInfo(FacetType.Convertibles).selectors + }); + cuts[5].functionSelectors[0] = ConvertiblesFacet.issueConvertible.selector; + cuts[5].functionSelectors[1] = ConvertiblesFacet.getConvertiblePosition.selector; + + // ------------------- Equity Compensation Facet ------------------- + cuts[6] = IDiamondCut.FacetCut({ + facetAddress: LibDeployment.deployFacet(FacetType.EquityCompensation), + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: LibDeployment.getFacetCutInfo(FacetType.EquityCompensation).selectors + }); + + // ------------------- Stock Plan Facet ------------------- + cuts[7] = IDiamondCut.FacetCut({ + facetAddress: LibDeployment.deployFacet(FacetType.StockPlan), + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: LibDeployment.getFacetCutInfo(FacetType.StockPlan).selectors + }); + + // ------------------- Warrant Facet ------------------- + cuts[8] = IDiamondCut.FacetCut({ + facetAddress: LibDeployment.deployFacet(FacetType.Warrant), + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: LibDeployment.getFacetCutInfo(FacetType.Warrant).selectors + }); + + // ------------------- Stakeholder NFT Facet ------------------- + cuts[9] = IDiamondCut.FacetCut({ + facetAddress: LibDeployment.deployFacet(FacetType.StakeholderNFT), + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: LibDeployment.getFacetCutInfo(FacetType.StakeholderNFT).selectors + }); + + // ------------------- Access Control Facet ------------------- + cuts[10] = IDiamondCut.FacetCut({ + facetAddress: LibDeployment.deployFacet(FacetType.AccessControl), + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: LibDeployment.getFacetCutInfo(FacetType.AccessControl).selectors + }); + + // Create reference diamond + CapTable referenceDiamond = new CapTable(owner, address(new DiamondCutFacet())); + + // Perform the cuts + DiamondCutFacet(address(referenceDiamond)).diamondCut(cuts, address(0), ""); + console.log("Cuts completed for reference diamond at:", address(referenceDiamond)); + return address(referenceDiamond); + } +} + +contract DeployFactoryScript is Script { + // runs locally on anvil + function run() external { + console.log("Deploying factory on anvil"); + string memory privateKeyStr = vm.envString("PRIVATE_KEY"); + if (bytes(privateKeyStr).length == 0) { + revert("Missing PRIVATE_KEY in .env"); + } + // Remove any whitespace and ensure 0x prefix + if (bytes(privateKeyStr)[0] != "0" || bytes(privateKeyStr)[1] != "x") { + revert("PRIVATE_KEY must start with 0x"); + } + uint256 deployerPrivateKey = vm.parseUint(privateKeyStr); + address deployerWallet = vm.addr(deployerPrivateKey); + + vm.startBroadcast(deployerWallet); + + // Try to get addresses from env + address referenceDiamond = vm.envOr("REFERENCE_DIAMOND", address(0)); + console.log("deployerWallet: ", deployerWallet); + + // Deploy new facets if addresses not in env + if (referenceDiamond == address(0)) { + console.log("Deploying new facets"); + referenceDiamond = LibDeployment.deployInitialFacets(deployerWallet); + } + + console.log("------- New Facet Addresses (Add to .env) -------"); + console.log("REFERENCE_DIAMOND=", referenceDiamond); + console.log("-------------------------------------------------"); + + // Deploy factory with facet addresses + CapTableFactory factory = new CapTableFactory(referenceDiamond); + console.log("\n------- Factory Address (add to .env) -------"); + console.log("FACTORY_ADDRESS=", address(factory)); + console.log("-------------------------------------------------"); + + vm.stopBroadcast(); + } + + function runProduction() external { + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + if (deployerPrivateKey == 0) { + revert("Missing PRIVATE_KEY in .env"); + } + address deployer = vm.addr(deployerPrivateKey); + console.log("Deploying DiamondCapTable system to Base Sepolia"); + + vm.startBroadcast(deployer); + + // Try to get addresses from env + address referenceDiamond = vm.envOr("REFERENCE_DIAMOND", address(0)); + + // Deploy new facets if addresses not in env + if (referenceDiamond == address(0)) { + revert("Missing REFERENCE_DIAMOND in .env"); + } + // Deploy factory with facet addresses + CapTableFactory factory = new CapTableFactory(referenceDiamond); + + console.log("\nCapTableFactory deployed at:", address(factory)); + + vm.stopPrank(); + // console.log("Diamond admin after accepting:", AccessControlFacet(diamond).getAdmin()); + // Verify factory is no longer admin + // console.log( + // "Factory is admin:", AccessControlFacet(diamond).hasRole(AccessControl.DEFAULT_ADMIN_ROLE, address(factory)) + // ); + vm.stopPrank(); + vm.stopBroadcast(); + } +} diff --git a/chain/script/SyncDiamonds.s.sol b/chain/script/SyncDiamonds.s.sol new file mode 100644 index 00000000..579660d1 --- /dev/null +++ b/chain/script/SyncDiamonds.s.sol @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "forge-std/Script.sol"; +import "forge-std/console.sol"; +import { CapTableFactory } from "@core/CapTableFactory.sol"; +import { IDiamondLoupe } from "diamond-3-hardhat/interfaces/IDiamondLoupe.sol"; +import { DiamondCutFacet } from "diamond-3-hardhat/facets/DiamondCutFacet.sol"; +import { IDiamondCut } from "diamond-3-hardhat/interfaces/IDiamondCut.sol"; +import { CapTable } from "@core/CapTable.sol"; + +library LibSyncDiamonds { + // After updating or creating a new facet, we need to ensure deployed instances are updated + function syncDiamond(address targetDiamond, address referenceDiamond) internal { + // Get current owner + IDiamondLoupe loupe = IDiamondLoupe(referenceDiamond); + IDiamondLoupe targetLoupe = IDiamondLoupe(targetDiamond); + + // Get all facets from reference + IDiamondLoupe.Facet[] memory referenceFacets = loupe.facets(); + + // Get all facets from target + IDiamondLoupe.Facet[] memory targetFacets = targetLoupe.facets(); + + console.log("target facets length: ", targetFacets.length); + console.log("reference facets length: ", referenceFacets.length); + + // Compare and create necessary cuts + for (uint256 i = 0; i < referenceFacets.length; i++) { + address refFacetAddr = referenceFacets[i].facetAddress; + bytes4[] memory refSelectors = referenceFacets[i].functionSelectors; + + // Check if any of these selectors already exist in target + bool[] memory selectorExists = new bool[](refSelectors.length); + uint256 newSelectorsCount = 0; + + for (uint256 k = 0; k < refSelectors.length; k++) { + bytes4 selector = refSelectors[k]; + bool exists = false; + + // Check if selector exists in any target facet + for (uint256 j = 0; j < targetFacets.length; j++) { + bytes4[] memory targetSelectors = targetFacets[j].functionSelectors; + for (uint256 m = 0; m < targetSelectors.length; m++) { + if (targetSelectors[m] == selector) { + exists = true; + break; + } + } + if (exists) break; + } + + selectorExists[k] = exists; + if (!exists) newSelectorsCount++; + } + + // If we found new selectors, add them + if (newSelectorsCount > 0) { + bytes4[] memory newSelectors = new bytes4[](newSelectorsCount); + uint256 index = 0; + for (uint256 k = 0; k < refSelectors.length; k++) { + if (!selectorExists[k]) { + newSelectors[index] = refSelectors[k]; + console.log("Adding selector:", uint32(refSelectors[k])); + index++; + } + } + + console.log("Adding", newSelectorsCount, "new selectors for facet:", refFacetAddr); + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); + cut[0] = IDiamondCut.FacetCut({ + facetAddress: refFacetAddr, + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: newSelectors + }); + DiamondCutFacet(targetDiamond).diamondCut(cut, address(0), ""); + console.log("Successfully added selectors"); + } + } + } +} + +contract SyncDiamondsScript is Script { + function run() external { + console.log("SyncDiamondsScript started"); + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + address referenceDiamond = vm.envAddress("REFERENCE_DIAMOND"); + address factory = vm.envAddress("FACTORY_ADDRESS"); + + vm.startBroadcast(deployerPrivateKey); + + // Get all deployed cap tables + CapTableFactory capTableFactory = CapTableFactory(factory); + uint256 count = capTableFactory.getCapTableCount(); + + // Sync each cap table + for (uint256 i = 0; i < count; i++) { + address capTable = capTableFactory.capTables(i); + LibSyncDiamonds.syncDiamond(capTable, referenceDiamond); + console.log("Synced cap table:", capTable); + } + + vm.stopBroadcast(); + console.log("SyncDiamondsScript completed"); + } +} diff --git a/chain/script/SyncFacets.s.sol b/chain/script/SyncFacets.s.sol new file mode 100644 index 00000000..2ddd0b44 --- /dev/null +++ b/chain/script/SyncFacets.s.sol @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "forge-std/Script.sol"; +import "forge-std/console.sol"; +import { IDiamondCut } from "diamond-3-hardhat/interfaces/IDiamondCut.sol"; +import { IDiamondLoupe } from "diamond-3-hardhat/interfaces/IDiamondLoupe.sol"; +import { LibDeployment } from "./DeployFactory.s.sol"; +import { DiamondLoupeFacet } from "diamond-3-hardhat/facets/DiamondLoupeFacet.sol"; +import { IssuerFacet } from "@facets/IssuerFacet.sol"; +import { StakeholderFacet } from "@facets/StakeholderFacet.sol"; +import { StockClassFacet } from "@facets/StockClassFacet.sol"; +import { StockFacet } from "@facets/StockFacet.sol"; +import { ConvertiblesFacet } from "@facets/ConvertiblesFacet.sol"; +import { EquityCompensationFacet } from "@facets/EquityCompensationFacet.sol"; +import { StockPlanFacet } from "@facets/StockPlanFacet.sol"; +import { WarrantFacet } from "@facets/WarrantFacet.sol"; +import { StakeholderNFTFacet } from "@facets/StakeholderNFTFacet.sol"; +import { AccessControlFacet } from "@facets/AccessControlFacet.sol"; + +library FacetHelper { + enum ChangeType { + Update, + Add, + Remove + } + + struct FacetChange { + bytes4 selector; + address currentAddress; + address newAddress; + ChangeType changeType; + bytes32 localCodeHash; + bytes32 remoteCodeHash; + } + + // Instead of mapping, use arrays and addresses + struct BytecodeHash { + address facetAddress; + bytes32 hash; + } + + function getFacetSelectors( + IDiamondLoupe.Facet[] memory facets, + bytes4 selector + ) + internal + pure + returns (bytes4[] memory) + { + for (uint256 i = 0; i < facets.length; i++) { + if (facets[i].functionSelectors[0] == selector) { + return facets[i].functionSelectors; + } + } + revert("Facet not found"); + } + + function getFacetBytecode(address facet) internal view returns (bytes memory) { + uint256 size; + assembly { + size := extcodesize(facet) + } + bytes memory code = new bytes(size); + assembly { + extcodecopy(facet, add(code, 0x20), 0, size) + } + return code; + } + + function getHashes(IDiamondLoupe.Facet[] memory facets) internal view returns (FacetHelper.BytecodeHash[] memory) { + FacetHelper.BytecodeHash[] memory hashes = new FacetHelper.BytecodeHash[](facets.length); + + for (uint256 i = 0; i < facets.length; i++) { + bytes memory code = FacetHelper.getFacetBytecode(facets[i].facetAddress); + hashes[i] = FacetHelper.BytecodeHash({ facetAddress: facets[i].facetAddress, hash: keccak256(code) }); + } + return hashes; + } + + function getHash(BytecodeHash[] memory hashes, address facetAddress) internal pure returns (bytes32) { + for (uint256 i = 0; i < hashes.length; i++) { + if (hashes[i].facetAddress == facetAddress) { + return hashes[i].hash; + } + } + return bytes32(0); + } + + function detectChanges( + IDiamondLoupe.Facet[] memory localFacets, + IDiamondLoupe.Facet[] memory deployedFacets, + BytecodeHash[] memory localHashes, + BytecodeHash[] memory remoteHashes + ) + internal + view + returns (FacetChange[] memory changes, uint256 changeCount) + { + changes = new FacetChange[](localFacets.length + deployedFacets.length); + + // Compare facets + for (uint256 i = 0; i < deployedFacets.length; i++) { + // Skip diamond cut facet + if (deployedFacets[i].functionSelectors[0] == IDiamondCut.diamondCut.selector) { + console.log("Skipping DiamondCut facet"); + continue; + } + + // Find matching facet by first selector + bool found = false; + for (uint256 j = 0; j < localFacets.length; j++) { + if (deployedFacets[i].functionSelectors[0] == localFacets[j].functionSelectors[0]) { + found = true; + + // Check if selectors match exactly + bool selectorsMatch = + deployedFacets[i].functionSelectors.length == localFacets[j].functionSelectors.length; + if (selectorsMatch) { + for (uint256 k = 0; k < deployedFacets[i].functionSelectors.length; k++) { + if (deployedFacets[i].functionSelectors[k] != localFacets[j].functionSelectors[k]) { + selectorsMatch = false; + break; + } + } + } + + // Force update if selectors don't match or if code hash is different + bytes32 localHash = getHash(localHashes, localFacets[j].facetAddress); + bytes32 remoteHash = getHash(remoteHashes, deployedFacets[i].facetAddress); + + if (!selectorsMatch || localHash != remoteHash) { + LibDeployment.FacetType facetType = + LibDeployment.getFacetTypeFromSelector(deployedFacets[i].functionSelectors[0]); + string memory facetName = LibDeployment.getFacetCutInfo(facetType).name; + console.log( + "\nForce updating facet", + facetName, + "due to:", + !selectorsMatch ? "selector mismatch" : "code change" + ); + + changes[changeCount] = FacetChange({ + selector: deployedFacets[i].functionSelectors[0], + currentAddress: deployedFacets[i].facetAddress, + newAddress: localFacets[j].facetAddress, + changeType: ChangeType.Update, + localCodeHash: localHash, + remoteCodeHash: remoteHash + }); + changeCount++; + } + break; + } + } + + // If not found in local, it needs to be removed + if (!found) { + changes[changeCount] = FacetChange({ + selector: deployedFacets[i].functionSelectors[0], + currentAddress: deployedFacets[i].facetAddress, + newAddress: address(0), + changeType: ChangeType.Remove, + localCodeHash: bytes32(0), + remoteCodeHash: bytes32(0) + }); + changeCount++; + } + } + + // Check for new facets + for (uint256 i = 0; i < localFacets.length; i++) { + if (localFacets[i].functionSelectors[0] == IDiamondCut.diamondCut.selector) { + continue; + } + + bool exists = false; + for (uint256 j = 0; j < deployedFacets.length; j++) { + if (localFacets[i].functionSelectors[0] == deployedFacets[j].functionSelectors[0]) { + exists = true; + break; + } + } + + if (!exists) { + changes[changeCount] = FacetChange({ + selector: localFacets[i].functionSelectors[0], + currentAddress: address(0), + newAddress: localFacets[i].facetAddress, + changeType: ChangeType.Add, + localCodeHash: bytes32(0), + remoteCodeHash: bytes32(0) + }); + changeCount++; + } + } + } +} + +contract SyncFacetsScript is Script { + using FacetHelper for *; + using LibDeployment for *; + + // Core facet operations + function addFacet(address diamond, address newFacet, bytes4[] memory selectors) public { + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); + cut[0] = IDiamondCut.FacetCut({ + facetAddress: newFacet, + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: selectors + }); + IDiamondCut(diamond).diamondCut(cut, address(0), ""); + } + + function replaceFacet(address diamond, address newFacet, bytes4[] memory selectors) public { + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); + cut[0] = IDiamondCut.FacetCut({ + facetAddress: newFacet, + action: IDiamondCut.FacetCutAction.Replace, + functionSelectors: selectors + }); + + try IDiamondCut(diamond).diamondCut(cut, address(0), "") { + console.log("Facet replaced successfully"); + } catch Error(string memory reason) { + console.log("Failed to replace facet:", reason); + revert(reason); + } catch (bytes memory) { + console.log("Failed to replace facet (no reason)"); + revert("Unknown error during facet replacement"); + } + } + + function removeFacet(address diamond, bytes4[] memory selectors) public { + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); + cut[0] = IDiamondCut.FacetCut({ + facetAddress: address(0), + action: IDiamondCut.FacetCutAction.Remove, + functionSelectors: selectors + }); + IDiamondCut(diamond).diamondCut(cut, address(0), ""); + } + + function processChanges( + FacetHelper.FacetChange memory change, + address referenceDiamond, + IDiamondLoupe.Facet[] memory deployedFacets, + IDiamondLoupe.Facet[] memory localFacets + ) + internal + { + LibDeployment.FacetType facetType = LibDeployment.getFacetTypeFromSelector(change.selector); + string memory facetName = LibDeployment.getFacetCutInfo(facetType).name; + + if (change.changeType == FacetHelper.ChangeType.Remove) { + console.log("\nRemoving facet:", facetName); + bytes4[] memory selectors = FacetHelper.getFacetSelectors(deployedFacets, change.selector); + removeFacet(referenceDiamond, selectors); + } else if (change.changeType == FacetHelper.ChangeType.Add) { + console.log("\nAdding facet:", facetName); + address newFacet = LibDeployment.deployFacet(facetType); + bytes4[] memory selectors = FacetHelper.getFacetSelectors(localFacets, change.selector); + addFacet(referenceDiamond, newFacet, selectors); + } else if (change.changeType == FacetHelper.ChangeType.Update) { + console.log("\nUpdating facet:", facetName); + address newFacet = LibDeployment.deployFacet(facetType); + bytes4[] memory selectors = FacetHelper.getFacetSelectors(deployedFacets, change.selector); + replaceFacet(referenceDiamond, newFacet, selectors); + } + } + + function run() external { + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + address referenceDiamond = vm.envAddress("REFERENCE_DIAMOND"); + string memory LOCAL_RPC = vm.envOr("LOCAL_RPC", string("http://localhost:8546")); + string memory REMOTE_RPC = vm.envOr("REMOTE_RPC", string("http://localhost:8545")); + console.log("LOCAL_RPC: %s", LOCAL_RPC); + console.log("REMOTE_RPC: %s", REMOTE_RPC); + + // Deploy locally to get latest implementations + uint256 localFork = vm.createFork(LOCAL_RPC); + vm.selectFork(localFork); + address localDiamond = LibDeployment.deployInitialFacets(address(this)); + IDiamondLoupe.Facet[] memory localFacets = IDiamondLoupe(localDiamond).facets(); + console.log("\nNumber of local facets: ", localFacets.length); + + // Get deployed facets from remote + uint256 remoteFork = vm.createFork(REMOTE_RPC); + vm.selectFork(remoteFork); + IDiamondLoupe.Facet[] memory deployedFacets = IDiamondLoupe(referenceDiamond).facets(); + console.log("Number of deployed facets: ", deployedFacets.length); + + // Pre-compute all bytecode hashes once + vm.selectFork(localFork); + FacetHelper.BytecodeHash[] memory localHashes = FacetHelper.getHashes(localFacets); + vm.selectFork(remoteFork); + FacetHelper.BytecodeHash[] memory remoteHashes = FacetHelper.getHashes(deployedFacets); + + // Now we can do pure comparison + (FacetHelper.FacetChange[] memory changes, uint256 changeCount) = + FacetHelper.detectChanges(localFacets, deployedFacets, localHashes, remoteHashes); + + if (changeCount > 0) { + console.log("\n=== Processing Changes ==="); + vm.selectFork(remoteFork); + vm.startBroadcast(deployerPrivateKey); + + for (uint256 i = 0; i < changeCount; i++) { + processChanges(changes[i], referenceDiamond, deployedFacets, localFacets); + } + + vm.stopBroadcast(); + console.log("\n=== Changes Completed ==="); + } + } +} diff --git a/chain/src/CapTable.sol b/chain/src/CapTable.sol deleted file mode 100644 index 0e169899..00000000 --- a/chain/src/CapTable.sol +++ /dev/null @@ -1,540 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import { AccessControlDefaultAdminRulesUpgradeable } from "openzeppelin-upgradeable/contracts/access/AccessControlDefaultAdminRulesUpgradeable.sol"; - -import { ICapTable } from "./interfaces/ICapTable.sol"; -import { StockTransferParams, Issuer, Stakeholder, StockClass, InitialShares, ActivePositions, SecIdsStockClass, StockLegendTemplate, StockParams, StockParamsQuantity, StockIssuanceParams } from "./lib/Structs.sol"; -import "./lib/transactions/Adjustment.sol"; -import "./lib/Stock.sol"; - -contract CapTable is ICapTable, AccessControlDefaultAdminRulesUpgradeable { - Issuer public issuer; - Stakeholder[] public stakeholders; - StockClass[] public stockClasses; - StockLegendTemplate[] public stockLegendTemplates; - - /// @inheritdoc ICapTable - bytes[] public override transactions; - - /// @dev Used to help generate deterministic UUIDs - uint256 public nonce; - - /// @inheritdoc ICapTable - mapping(bytes16 => uint256) public override stakeholderIndex; - /// @inheritdoc ICapTable - mapping(bytes16 => uint256) public override stockClassIndex; - /// @inheritdoc ICapTable - mapping(address => bytes16) public override walletsPerStakeholder; - - ActivePositions positions; - SecIdsStockClass activeSecs; - - /// @inheritdoc ICapTable - bytes32 public constant override ADMIN_ROLE = keccak256("ADMIN"); - /// @inheritdoc ICapTable - bytes32 public constant override OPERATOR_ROLE = keccak256("OPERATOR"); - - event IssuerCreated(bytes16 indexed id, string indexed _name); - event StakeholderCreated(bytes16 indexed id); - event StockClassCreated(bytes16 indexed id, string indexed classType, uint256 indexed pricePerShare, uint256 initialSharesAuthorized); - - error StakeholderAlreadyExists(bytes16 stakeholder_id); - error StockClassAlreadyExists(bytes16 stock_class_id); - error StockClassDoesNotExist(bytes16 stock_class_id); - error InvalidWallet(address wallet); - error NoStakeholder(bytes16 stakeholder_id); - error InvalidStockClass(bytes16 stock_class_id); - error InsufficientIssuerSharesAuthorized(); - error InsufficientStockClassSharesAuthorized(); - error NoIssuanceFound(); - error WalletAlreadyExists(address wallet); - error NoActivePositionFound(); - - constructor() { - _disableInitializers(); - } - - function initialize(bytes16 id, string memory name, uint256 initial_shares_authorized, address admin) external initializer { - __AccessControlDefaultAdminRules_init(0 seconds, admin); - _grantRole(ADMIN_ROLE, admin); - _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE); - _setRoleAdmin(OPERATOR_ROLE, ADMIN_ROLE); - - issuer = Issuer(id, name, 0, initial_shares_authorized); - emit IssuerCreated(id, name); - } - - /// @inheritdoc ICapTable - function getTransactionsCount() external view returns (uint256) { - return transactions.length; - } - - /// @inheritdoc ICapTable - function getTotalActiveSecuritiesCount() external view returns (uint256) { - uint256 count = 0; - for (uint256 i = 0; i < stakeholders.length; i++) { - for (uint256 j = 0; j < stockClasses.length; j++) { - count += activeSecs.activeSecurityIdsByStockClass[stakeholders[i].id][stockClasses[j].id].length; - } - } - return count; - } - - /// @inheritdoc ICapTable - function seedSharesAuthorizedAndIssued(InitialShares calldata params) external override { - require( - params.issuerInitialShares.shares_authorized > 0 && - params.issuerInitialShares.shares_issued > 0 && - params.stockClassesInitialShares.length > 0, - "Invalid Seeding Shares Params" - ); - - issuer.shares_authorized = params.issuerInitialShares.shares_authorized; - issuer.shares_issued = params.issuerInitialShares.shares_issued; - - for (uint256 i = 0; i < params.stockClassesInitialShares.length; i++) { - bytes16 stockClassId = params.stockClassesInitialShares[i].id; - _checkInvalidStockClass(stockClassId); - - uint256 index = stockClassIndex[stockClassId] - 1; - stockClasses[index].shares_authorized = params.stockClassesInitialShares[i].shares_authorized; - stockClasses[index].shares_issued = params.stockClassesInitialShares[i].shares_issued; - } - } - - /// @inheritdoc ICapTable - function seedMultipleActivePositionsAndSecurityIds( - bytes16[] calldata stakeholderIds, - bytes16[] calldata securityIds, - bytes16[] calldata stockClassIds, - uint256[] calldata quantities, - uint256[] calldata sharePrices, - uint40[] calldata timestamps - ) external override onlyAdmin { - require( - stakeholderIds.length == securityIds.length && - securityIds.length == stockClassIds.length && - stockClassIds.length == quantities.length && - quantities.length == sharePrices.length && - sharePrices.length == timestamps.length, - "Input arrays must have the same length" - ); - - for (uint256 i = 0; i < stakeholderIds.length; i++) { - // perform requires to ensure valid stakeholders and stock classes - _checkStakeholderIsStored(stakeholderIds[i]); - _checkInvalidStockClass(stockClassIds[i]); - positions.activePositions[stakeholderIds[i]][securityIds[i]] = ActivePosition( - stockClassIds[i], - quantities[i], - sharePrices[i], - timestamps[i] - ); - - activeSecs.activeSecurityIdsByStockClass[stakeholderIds[i]][stockClassIds[i]].push(securityIds[i]); - } - } - - /// @inheritdoc ICapTable - function createStakeholder(bytes16 _id, string memory _stakeholder_type, string memory _current_relationship) external override onlyAdmin { - _checkStakeholderExists(_id); - - stakeholders.push(Stakeholder(_id, _stakeholder_type, _current_relationship)); - stakeholderIndex[_id] = stakeholders.length; - emit StakeholderCreated(_id); - } - - /// @inheritdoc ICapTable - function createStockClass( - bytes16 _id, - string memory _class_type, - uint256 _price_per_share, - uint256 _initial_share_authorized - ) external override onlyAdmin { - _checkStockClassExists(_id); - - stockClasses.push(StockClass(_id, _class_type, _price_per_share, 0, _initial_share_authorized)); - stockClassIndex[_id] = stockClasses.length; - emit StockClassCreated(_id, _class_type, _price_per_share, _initial_share_authorized); - } - - /// @inheritdoc ICapTable - // Basic functionality of Stock Legend Template, unclear how it ties to active positions. - function createStockLegendTemplate(bytes16 _id) external override onlyAdmin { - stockLegendTemplates.push(StockLegendTemplate(_id)); - } - - /// @inheritdoc ICapTable - /// @notice Setter for walletsPerStakeholder mapping - /// @dev Function is separate from createStakeholder since multiple wallets will be added per stakeholder at different times. - function addWalletToStakeholder(bytes16 _stakeholder_id, address _wallet) external override onlyOperator { - _checkInvalidWallet(_wallet); - _checkStakeholderIsStored(_stakeholder_id); - _checkWalletAlreadyExists(_wallet); - - walletsPerStakeholder[_wallet] = _stakeholder_id; - } - - /// @inheritdoc ICapTable - /// @notice Removing wallet from walletsPerStakeholder mapping - function removeWalletFromStakeholder(bytes16 _stakeholder_id, address _wallet) external override onlyOperator { - _checkInvalidWallet(_wallet); - _checkStakeholderIsStored(_stakeholder_id); - - delete walletsPerStakeholder[_wallet]; - } - - /// @inheritdoc ICapTable - function issueStock(StockIssuanceParams calldata params) external override onlyOperator { - _checkStakeholderIsStored(params.stakeholder_id); - _checkInvalidStockClass(params.stock_class_id); - - StockClass storage stockClass = stockClasses[stockClassIndex[params.stock_class_id] - 1]; - - require(issuer.shares_issued + params.quantity <= issuer.shares_authorized, "Issuer: Insufficient shares authorized"); - require(stockClass.shares_issued + params.quantity <= stockClass.shares_authorized, "StockClass: Insufficient shares authorized"); - - nonce++; - - StockLib.createIssuance(nonce, params, positions, activeSecs, transactions, issuer, stockClass); - } - - /// @inheritdoc ICapTable - function repurchaseStock(StockParams calldata params, uint256 quantity, uint256 price) external override onlyOperator { - _checkStakeholderIsStored(params.stakeholder_id); - _checkInvalidStockClass(params.stock_class_id); - - nonce++; - - StockParamsQuantity memory repurchaseParams = StockParamsQuantity( - nonce, - quantity, - params.stakeholder_id, - params.stock_class_id, - params.security_id, - params.comments, - params.reason_text - ); - - StockLib.createRepurchase( - repurchaseParams, - price, - positions, - activeSecs, - transactions, - issuer, - stockClasses[stockClassIndex[params.stock_class_id] - 1] - ); - } - - /// @inheritdoc ICapTable - function retractStockIssuance(StockParams calldata params) external override onlyOperator { - _checkStakeholderIsStored(params.stakeholder_id); - _checkInvalidStockClass(params.stock_class_id); - - nonce++; - - StockLib.createRetraction( - params, - nonce, - positions, - activeSecs, - transactions, - issuer, - stockClasses[stockClassIndex[params.stock_class_id] - 1] - ); - } - - /// @inheritdoc ICapTable - function reissueStock(StockParams calldata params, bytes16[] memory resulting_security_ids) external override onlyOperator { - _checkStakeholderIsStored(params.stakeholder_id); - _checkInvalidStockClass(params.stock_class_id); - _checkResultingSecurityIds(resulting_security_ids, params.stakeholder_id, params.stock_class_id); - - nonce++; - - StockLib.createReissuance( - params, - nonce, - resulting_security_ids, - positions, - activeSecs, - transactions, - issuer, - stockClasses[stockClassIndex[params.stock_class_id] - 1] - ); - } - - /// @inheritdoc ICapTable - function cancelStock(StockParams calldata params, uint256 quantity) external override onlyOperator { - _checkStakeholderIsStored(params.stakeholder_id); - _checkInvalidStockClass(params.stock_class_id); - - nonce++; - - StockParamsQuantity memory cancelParams = StockParamsQuantity( - nonce, - quantity, - params.stakeholder_id, - params.stock_class_id, - params.security_id, - params.comments, - params.reason_text - ); - - StockLib.createCancellation( - cancelParams, - positions, - activeSecs, - transactions, - issuer, - stockClasses[stockClassIndex[params.stock_class_id] - 1] - ); - } - - /// @inheritdoc ICapTable - function transferStock( - bytes16 transferorStakeholderId, - bytes16 transfereeStakeholderId, - bytes16 stockClassId, - bool isBuyerVerified, - uint256 quantity, - uint256 share_price - ) external override onlyOperator { - _checkStakeholderIsStored(transferorStakeholderId); - _checkStakeholderIsStored(transfereeStakeholderId); - _checkInvalidStockClass(stockClassId); - - nonce++; - - StockTransferParams memory params = StockTransferParams( - transferorStakeholderId, - transfereeStakeholderId, - stockClassId, - isBuyerVerified, - quantity, - share_price, - nonce - ); - - StockLib.createTransfer(params, positions, activeSecs, transactions, issuer, stockClasses[stockClassIndex[stockClassId] - 1]); - } - - /// @inheritdoc ICapTable - // Stock Acceptance does not impact an active position. It's only recorded. - function acceptStock(bytes16 stakeholderId, bytes16 stockClassId, bytes16 securityId, string[] memory comments) external override onlyOperator { - _checkStakeholderIsStored(stakeholderId); - _checkInvalidStockClass(stockClassId); - - nonce++; - - ActivePosition memory activePosition = positions.activePositions[stakeholderId][securityId]; - - _checkActivePositionExists(activePosition); - - StockLib.createAcceptance(nonce, securityId, comments, transactions); - } - - /// @inheritdoc ICapTable - function adjustIssuerAuthorizedShares( - uint256 newSharesAuthorized, - string[] memory comments, - string memory boardApprovalDate, - string memory stockholderApprovalDate - ) external override onlyAdmin { - require(newSharesAuthorized >= issuer.shares_issued, "InsufficientIssuerSharesAuthorized: shares_issued exceeds newSharesAuthorized"); - - nonce++; - - Adjustment.adjustIssuerAuthorizedShares( - nonce, - newSharesAuthorized, - comments, - boardApprovalDate, - stockholderApprovalDate, - issuer, - transactions - ); - } - - /// @inheritdoc ICapTable - function adjustStockClassAuthorizedShares( - bytes16 stockClassId, - uint256 newAuthorizedShares, - string[] memory comments, - string memory boardApprovalDate, - string memory stockholderApprovalDate - ) external override onlyAdmin { - StockClass storage stockClass = stockClasses[stockClassIndex[stockClassId] - 1]; - _checkInvalidStockClass(stockClassId); - // check that the new stock class authorized is less than the issuer authorized if not revert - require( - newAuthorizedShares <= issuer.shares_authorized, - "InsufficientStockClassSharesAuthorized: stock class authorized shares exceeds issuer shares authorized" - ); - - nonce++; - - Adjustment.adjustStockClassAuthorizedShares( - nonce, - newAuthorizedShares, - comments, - boardApprovalDate, - stockholderApprovalDate, - stockClass, - transactions - ); - } - - /// @inheritdoc ICapTable - function getStakeholderById(bytes16 _id) external view override returns (bytes16, string memory, string memory) { - if (stakeholderIndex[_id] > 0) { - Stakeholder memory stakeholder = stakeholders[stakeholderIndex[_id] - 1]; - return (stakeholder.id, stakeholder.stakeholder_type, stakeholder.current_relationship); - } else { - return ("", "", ""); - } - } - - /// @inheritdoc ICapTable - function getStockClassById(bytes16 _id) external view override returns (bytes16, string memory, uint256, uint256, uint256) { - if (stockClassIndex[_id] > 0) { - StockClass memory stockClass = stockClasses[stockClassIndex[_id] - 1]; - return (stockClass.id, stockClass.class_type, stockClass.price_per_share, stockClass.shares_issued, stockClass.shares_authorized); - } else { - return ("", "", 0, 0, 0); - } - } - - /// @inheritdoc ICapTable - function getStakeholderIdByWallet(address _wallet) external view override returns (bytes16 stakeholderId) { - require(walletsPerStakeholder[_wallet] != bytes16(0), "No stakeholder found"); - return walletsPerStakeholder[_wallet]; - } - - /// @inheritdoc ICapTable - function getTotalNumberOfStakeholders() external view override returns (uint256) { - return stakeholders.length; - } - - /// @inheritdoc ICapTable - function getTotalNumberOfStockClasses() external view override returns (uint256) { - return stockClasses.length; - } - - /// @inheritdoc ICapTable - function getActivePosition(bytes16 stakeholderId, bytes16 securityId) external view returns (bytes16, uint, uint, uint40) { - ActivePosition storage position = positions.activePositions[stakeholderId][securityId]; - return (position.stock_class_id, position.quantity, position.share_price, position.timestamp); - } - - /// @inheritdoc ICapTable - function getAveragePosition(bytes16 stakeholderId, bytes16 stockClassId) external view returns (uint, uint, uint40) { - bytes16[] memory activeSecurityIDs = activeSecs.activeSecurityIdsByStockClass[stakeholderId][stockClassId]; - uint quantityPrice = 0; - uint quantity = 0; - uint40 timestamp = 0; - for (uint i = 0; i < activeSecurityIDs.length; i++) { - ActivePosition storage position = positions.activePositions[stakeholderId][activeSecurityIDs[i]]; - // Alley-oop the web2 caller to find the avg to avoid issues with fractions - quantityPrice += position.quantity * position.share_price; - quantity += position.quantity; - timestamp = position.timestamp > timestamp ? position.timestamp : timestamp; - } - return (quantityPrice, quantity, timestamp); - } - - /* Role Based Access Control */ - modifier onlyOperator() { - /// @notice Admins are also considered Operators - require(hasRole(OPERATOR_ROLE, _msgSender()) || _isAdmin(), "Does not have operator role"); - _; - } - - modifier onlyAdmin() { - require(_isAdmin(), "Does not have admin role"); - _; - } - - function _isAdmin() internal view returns (bool) { - return hasRole(ADMIN_ROLE, _msgSender()); - } - - // External API for updating roles of addresses - - /// @inheritdoc ICapTable - function addAdmin(address addr) external override onlyAdmin { - _grantRole(ADMIN_ROLE, addr); - } - - /// @inheritdoc ICapTable - function removeAdmin(address addr) external override onlyAdmin { - _revokeRole(ADMIN_ROLE, addr); - } - - /// @inheritdoc ICapTable - function addOperator(address addr) external override onlyAdmin { - _grantRole(OPERATOR_ROLE, addr); - } - - /// @inheritdoc ICapTable - function removeOperator(address addr) external override onlyAdmin { - _revokeRole(OPERATOR_ROLE, addr); - } - - function _checkStakeholderExists(bytes16 _id) internal view { - if (stakeholderIndex[_id] > 0) { - revert StakeholderAlreadyExists(_id); - } - } - - function _checkStockClassExists(bytes16 _id) internal view { - if (stockClassIndex[_id] > 0) { - revert StockClassAlreadyExists(_id); - } - } - - function _checkInvalidWallet(address _wallet) internal pure { - if (_wallet == address(0)) { - revert InvalidWallet(_wallet); - } - } - - function _checkStakeholderIsStored(bytes16 _id) internal view { - if (stakeholderIndex[_id] == 0) { - revert NoStakeholder(_id); - } - } - - function _checkWalletAlreadyExists(address _wallet) internal view { - if (walletsPerStakeholder[_wallet] != bytes16(0)) { - revert WalletAlreadyExists(_wallet); - } - } - - function _checkInvalidStockClass(bytes16 _stock_class_id) internal view { - if (stockClassIndex[_stock_class_id] == 0) { - revert InvalidStockClass(_stock_class_id); - } - } - - function _checkResultingSecurityIds(bytes16[] memory resulting_security_ids, bytes16 stakeholder_id, bytes16 stock_class_id) internal view { - if (resulting_security_ids.length == 0) { - revert NoIssuanceFound(); - } - - bytes16 security_id = resulting_security_ids[0]; - ActivePosition memory activePosition = positions.activePositions[stakeholder_id][security_id]; - - if (activePosition.quantity == 0 || activePosition.stock_class_id != stock_class_id) { - revert NoActivePositionFound(); - } - } - - function _checkActivePositionExists(ActivePosition memory activePosition) internal pure { - if (activePosition.quantity == 0) { - revert NoActivePositionFound(); - } - } -} diff --git a/chain/src/CapTableFactory.sol b/chain/src/CapTableFactory.sol deleted file mode 100644 index 45ba9dd5..00000000 --- a/chain/src/CapTableFactory.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import { UpgradeableBeacon } from "openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; -import { BeaconProxy } from "openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; -import { Ownable } from "openzeppelin/contracts/access/Ownable.sol"; -import { ICapTableFactory } from "./interfaces/ICapTableFactory.sol"; -import { ICapTable } from "./interfaces/ICapTable.sol"; - -contract CapTableFactory is ICapTableFactory, Ownable { - address public capTableImplementation; - UpgradeableBeacon public capTableBeacon; - address[] public capTableProxies; - - constructor(address _capTableImplementation) { - require(_capTableImplementation != address(0), "Invalid implementation address"); - capTableImplementation = _capTableImplementation; - capTableBeacon = new UpgradeableBeacon(capTableImplementation); - } - - function createCapTable(bytes16 id, string memory name, uint256 initial_shares_authorized) external onlyOwner returns (address) { - require(id != bytes16(0) && initial_shares_authorized != 0, "Invalid issuer params"); - - bytes memory initializationData = abi.encodeCall(ICapTable.initialize, (id, name, initial_shares_authorized, msg.sender)); - BeaconProxy capTableProxy = new BeaconProxy(address(capTableBeacon), initializationData); - capTableProxies.push(address(capTableProxy)); - emit CapTableCreated(address(capTableProxy)); - return address(capTableProxy); - } - - function updateCapTableImplementation(address newImplementation) external onlyOwner { - require(newImplementation != address(0), "Invalid implementation address"); - capTableBeacon.upgradeTo(newImplementation); - capTableImplementation = newImplementation; - } - - function getCapTableCount() external view returns (uint256) { - return capTableProxies.length; - } -} diff --git a/chain/src/core/CapTable.sol b/chain/src/core/CapTable.sol index 3036823a..31c04040 100644 --- a/chain/src/core/CapTable.sol +++ b/chain/src/core/CapTable.sol @@ -2,9 +2,14 @@ pragma solidity ^0.8.0; import { LibDiamond } from "diamond-3-hardhat/libraries/LibDiamond.sol"; -import { IDiamondCut } from "diamond-3-hardhat/interfaces/IDiamondCut.sol"; import { Diamond } from "diamond-3-hardhat/Diamond.sol"; contract CapTable is Diamond { - constructor(address _contractOwner, address _diamondCutFacet) Diamond(_contractOwner, _diamondCutFacet) {} + constructor(address _owner, address _diamondCutFacet) Diamond(_owner, _diamondCutFacet) { } + + function transferOwner(address newOwner) public { + LibDiamond.enforceIsContractOwner(); + // Only called by the owner + LibDiamond.setContractOwner(newOwner); + } } diff --git a/chain/src/core/CapTableFactory.sol b/chain/src/core/CapTableFactory.sol index e40882a1..670acd24 100644 --- a/chain/src/core/CapTableFactory.sol +++ b/chain/src/core/CapTableFactory.sol @@ -2,157 +2,77 @@ pragma solidity ^0.8.20; import { CapTable } from "./CapTable.sol"; +import { IDiamondLoupe } from "diamond-3-hardhat/interfaces/IDiamondLoupe.sol"; import { DiamondCutFacet } from "diamond-3-hardhat/facets/DiamondCutFacet.sol"; import { IDiamondCut } from "diamond-3-hardhat/interfaces/IDiamondCut.sol"; import { IssuerFacet } from "@facets/IssuerFacet.sol"; -import { StakeholderFacet } from "@facets/StakeholderFacet.sol"; -import { StockClassFacet } from "@facets/StockClassFacet.sol"; -import { StockFacet } from "@facets/StockFacet.sol"; -import { ConvertiblesFacet } from "@facets/ConvertiblesFacet.sol"; -import { EquityCompensationFacet } from "@facets/EquityCompensationFacet.sol"; -import { StockPlanFacet } from "@facets/StockPlanFacet.sol"; -import { WarrantFacet } from "@facets/WarrantFacet.sol"; -import { StakeholderNFTFacet } from "@facets/StakeholderNFTFacet.sol"; -import "forge-std/console.sol"; - -contract CapTableFactory { +import { AccessControlFacet } from "@facets/AccessControlFacet.sol"; +import { AccessControl } from "@libraries/AccessControl.sol"; +import { Ownable } from "openzeppelin-contracts/contracts/access/Ownable.sol"; +import { DiamondLoupeFacet } from "diamond-3-hardhat/facets/DiamondLoupeFacet.sol"; + +contract CapTableFactory is Ownable { event CapTableCreated(address indexed capTable, bytes16 indexed issuerId); address[] public capTables; - // Store facet addresses - address public immutable diamondCutFacet; - address public immutable issuerFacet; - address public immutable stakeholderFacet; - address public immutable stockClassFacet; - address public immutable stockFacet; - address public immutable convertiblesFacet; - address public immutable equityCompensationFacet; - address public immutable stockPlanFacet; - address public immutable warrantFacet; - address public immutable stakeholderNFTFacet; - - constructor( - address _diamondCutFacet, - address _issuerFacet, - address _stakeholderFacet, - address _stockClassFacet, - address _stockFacet, - address _convertiblesFacet, - address _equityCompensationFacet, - address _stockPlanFacet, - address _warrantFacet, - address _stakeholderNFTFacet - ) { - require(_diamondCutFacet != address(0), "Invalid diamondCutFacet"); - diamondCutFacet = _diamondCutFacet; - issuerFacet = _issuerFacet; - stakeholderFacet = _stakeholderFacet; - stockClassFacet = _stockClassFacet; - stockFacet = _stockFacet; - convertiblesFacet = _convertiblesFacet; - equityCompensationFacet = _equityCompensationFacet; - stockPlanFacet = _stockPlanFacet; - warrantFacet = _warrantFacet; - stakeholderNFTFacet = _stakeholderNFTFacet; + // Reference diamond to copy facets from + address public immutable referenceDiamond; + + constructor(address _referenceDiamond) { + require(_referenceDiamond != address(0), "Invalid referenceDiamond"); + referenceDiamond = _referenceDiamond; } - function createCapTable(bytes16 id, uint256 initialSharesAuthorized) external returns (address) { + function createCapTable(bytes16 id, uint256 initialSharesAuthorized) external onlyOwner returns (address) { require(id != bytes16(0) && initialSharesAuthorized != 0, "Invalid issuer params"); - // Deploy Diamond with factory as the owner - console.log("inside createCapTable"); - console.log("msg.sender: ", msg.sender); - console.log("factory address (this): ", address(this)); + // Get DiamondCutFacet address from reference diamond using loupe + DiamondLoupeFacet loupe = DiamondLoupeFacet(referenceDiamond); + address diamondCutFacet = loupe.facetAddress(IDiamondCut.diamondCut.selector); - // Make the factory the owner, not msg.sender + // Create CapTable with factory as initial owner CapTable diamond = new CapTable(address(this), diamondCutFacet); - // Create facet cuts in memory - IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](9); - - // IssuerFacet - bytes4[] memory issuerSelectors = new bytes4[](2); - issuerSelectors[0] = IssuerFacet.initializeIssuer.selector; - issuerSelectors[1] = IssuerFacet.adjustIssuerAuthorizedShares.selector; - cuts[0] = IDiamondCut.FacetCut({ facetAddress: issuerFacet, action: IDiamondCut.FacetCutAction.Add, functionSelectors: issuerSelectors }); - - // StakeholderFacet - bytes4[] memory stakeholderSelectors = new bytes4[](3); - stakeholderSelectors[0] = StakeholderFacet.createStakeholder.selector; - stakeholderSelectors[1] = StakeholderFacet.linkStakeholderAddress.selector; - stakeholderSelectors[2] = StakeholderFacet.getStakeholderPositions.selector; - cuts[1] = IDiamondCut.FacetCut({ - facetAddress: stakeholderFacet, - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: stakeholderSelectors - }); - - // StockClassFacet - bytes4[] memory stockClassSelectors = new bytes4[](2); - stockClassSelectors[0] = StockClassFacet.createStockClass.selector; - stockClassSelectors[1] = StockClassFacet.adjustAuthorizedShares.selector; - cuts[2] = IDiamondCut.FacetCut({ - facetAddress: stockClassFacet, - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: stockClassSelectors - }); - - // StockFacet - bytes4[] memory stockSelectors = new bytes4[](1); - stockSelectors[0] = StockFacet.issueStock.selector; - cuts[3] = IDiamondCut.FacetCut({ facetAddress: stockFacet, action: IDiamondCut.FacetCutAction.Add, functionSelectors: stockSelectors }); - - // ConvertiblesFacet - bytes4[] memory convertibleSelectors = new bytes4[](2); - convertibleSelectors[0] = ConvertiblesFacet.issueConvertible.selector; - convertibleSelectors[1] = ConvertiblesFacet.getConvertiblePosition.selector; - cuts[4] = IDiamondCut.FacetCut({ - facetAddress: convertiblesFacet, - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: convertibleSelectors - }); - - // EquityCompensationFacet - bytes4[] memory equityCompensationSelectors = new bytes4[](3); - equityCompensationSelectors[0] = EquityCompensationFacet.issueEquityCompensation.selector; - equityCompensationSelectors[1] = EquityCompensationFacet.getPosition.selector; - equityCompensationSelectors[2] = EquityCompensationFacet.exerciseEquityCompensation.selector; - cuts[5] = IDiamondCut.FacetCut({ - facetAddress: equityCompensationFacet, - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: equityCompensationSelectors - }); - - // StockPlanFacet - bytes4[] memory stockPlanSelectors = new bytes4[](2); - stockPlanSelectors[0] = StockPlanFacet.createStockPlan.selector; - stockPlanSelectors[1] = StockPlanFacet.adjustStockPlanPool.selector; - cuts[6] = IDiamondCut.FacetCut({ - facetAddress: stockPlanFacet, - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: stockPlanSelectors - }); - - // WarrantFacet - bytes4[] memory warrantSelectors = new bytes4[](2); - warrantSelectors[0] = WarrantFacet.issueWarrant.selector; - warrantSelectors[1] = WarrantFacet.getWarrantPosition.selector; - cuts[7] = IDiamondCut.FacetCut({ facetAddress: warrantFacet, action: IDiamondCut.FacetCutAction.Add, functionSelectors: warrantSelectors }); - - // StakeholderNFTFacet - bytes4[] memory stakeholderNFTSelectors = new bytes4[](2); - stakeholderNFTSelectors[0] = StakeholderNFTFacet.mint.selector; - stakeholderNFTSelectors[1] = StakeholderNFTFacet.tokenURI.selector; - cuts[8] = IDiamondCut.FacetCut({ - facetAddress: stakeholderNFTFacet, - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: stakeholderNFTSelectors - }); + // Get facet information from reference diamond + IDiamondLoupe.Facet[] memory existingFacets = loupe.facets(); + + // Count valid facets (excluding DiamondCut) + uint256 validFacetCount = 0; + for (uint256 i = 0; i < existingFacets.length; i++) { + bytes4 firstSelector = existingFacets[i].functionSelectors[0]; + // Skip if this is the DiamondCut facet + if (firstSelector != DiamondCutFacet.diamondCut.selector) { + validFacetCount++; + } + } + + // Create cuts array for valid facets + IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](validFacetCount); + uint256 cutIndex = 0; + + for (uint256 i = 0; i < existingFacets.length; i++) { + bytes4 firstSelector = existingFacets[i].functionSelectors[0]; + // Skip if this is the DiamondCut facet + if (firstSelector != DiamondCutFacet.diamondCut.selector) { + cuts[cutIndex] = IDiamondCut.FacetCut({ + facetAddress: existingFacets[i].facetAddress, + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: existingFacets[i].functionSelectors + }); + cutIndex++; + } + } // Perform the cuts DiamondCutFacet(address(diamond)).diamondCut(cuts, address(0), ""); + // Initialize access control first - this makes the factory the admin + AccessControlFacet(address(diamond)).initializeAccessControl(); + + // Grant the diamond the OPERATOR_ROLE - Necessary for the NFT facet to work + AccessControlFacet(address(diamond)).grantRole(AccessControl.OPERATOR_ROLE, address(diamond)); + // Initialize the issuer IssuerFacet(address(diamond)).initializeIssuer(id, initialSharesAuthorized); @@ -160,6 +80,12 @@ contract CapTableFactory { capTables.push(address(diamond)); emit CapTableCreated(address(diamond), id); + + // Transfer Diamond ownership to msg.sender + CapTable(payable(diamond)).transferOwner(msg.sender); + // Transfer AccessControlFacet admin to msg.sender + AccessControlFacet(address(diamond)).transferAdmin(msg.sender); + return address(diamond); } diff --git a/chain/src/core/Storage.sol b/chain/src/core/Storage.sol index 223d961f..cfbb404a 100644 --- a/chain/src/core/Storage.sol +++ b/chain/src/core/Storage.sol @@ -1,8 +1,23 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { StockActivePositions, ConvertibleActivePositions, EquityCompensationActivePositions, WarrantActivePositions, Issuer, StockClass, StockPlan } from "@libraries/Structs.sol"; +import { + StockActivePositions, + ConvertibleActivePositions, + EquityCompensationActivePositions, + WarrantActivePositions, + Issuer, + StockClass, + StockPlan +} from "@libraries/Structs.sol"; + struct Storage { + // Access Control storage + mapping(bytes32 => mapping(address => bool)) roles; + mapping(bytes32 => bytes32) roleAdmin; // hierarchy of roles + address currentAdmin; // Current admin address + address pendingAdmin; // Pending admin address for ownership transfer + // Existing storage Issuer issuer; bytes16[] stakeholders; mapping(bytes16 => uint256) stakeholderIndex; diff --git a/chain/src/facets/AccessControlFacet.sol b/chain/src/facets/AccessControlFacet.sol new file mode 100644 index 00000000..9a721778 --- /dev/null +++ b/chain/src/facets/AccessControlFacet.sol @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Storage, StorageLib } from "@core/Storage.sol"; +import { AccessControlUpgradeable } from + "openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol"; +import { IAccessControlFacet } from "@interfaces/IAccessControlFacet.sol"; + +contract AccessControlFacet is AccessControlUpgradeable, IAccessControlFacet { + // Role definitions + bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); // For protocols and issuer + bytes32 public constant INVESTOR_ROLE = keccak256("INVESTOR_ROLE"); // For shareholders/stakeholders + + /// @notice Initialize the access control system + /// @dev Sets up initial roles. The deployer (CapTableFactory) gets admin role + function initializeAccessControl() external { + Storage storage ds = StorageLib.get(); + + // Set up admin role for the deployer (factory) + ds.roles[DEFAULT_ADMIN_ROLE][msg.sender] = true; + ds.currentAdmin = msg.sender; // Set initial admin + emit RoleGranted(DEFAULT_ADMIN_ROLE, msg.sender, msg.sender); + + // Set up role admins using helper function + _setRoleAdmin(OPERATOR_ROLE, DEFAULT_ADMIN_ROLE); + _setRoleAdmin(INVESTOR_ROLE, DEFAULT_ADMIN_ROLE); + + // Note: We don't need to explicitly grant OPERATOR and INVESTOR roles + // because _grantRole automatically grants them when granting DEFAULT_ADMIN_ROLE + } + + /// @dev Override hasRole to use diamond storage + function hasRole( + bytes32 role, + address account + ) + public + view + virtual + override(AccessControlUpgradeable, IAccessControlFacet) + returns (bool) + { + return StorageLib.get().roles[role][account]; + } + + /// @dev Override getRoleAdmin to use diamond storage + function getRoleAdmin(bytes32 role) + public + view + virtual + override(AccessControlUpgradeable, IAccessControlFacet) + returns (bytes32) + { + return StorageLib.get().roleAdmin[role]; + } + + /// @notice Grants `role` to `account` + /// @dev Caller must have admin role for `role` + function grantRole( + bytes32 role, + address account + ) + public + virtual + override(AccessControlUpgradeable, IAccessControlFacet) + { + if (!hasRole(getRoleAdmin(role), msg.sender)) { + revert AccessControlUnauthorized(msg.sender, getRoleAdmin(role)); + } + _grantRole(role, account); + } + + /// @notice Revokes `role` from `account` + /// @dev Caller must have admin role for `role` + function revokeRole( + bytes32 role, + address account + ) + public + virtual + override(AccessControlUpgradeable, IAccessControlFacet) + { + if (!hasRole(getRoleAdmin(role), msg.sender)) { + revert AccessControlUnauthorized(msg.sender, getRoleAdmin(role)); + } + _revokeRole(role, account); + } + + /// @notice Renounces `role` for the calling account + /// @dev Calling account must have the role + function renounceRole( + bytes32 role, + address account + ) + public + virtual + override(AccessControlUpgradeable, IAccessControlFacet) + { + if (account != msg.sender) { + revert AccessControlBadConfirmation(); + } + _revokeRole(role, account); + } + + /// @dev Override _grantRole to use diamond storage + function _grantRole(bytes32 role, address account) internal virtual override { + Storage storage ds = StorageLib.get(); + if (!ds.roles[role][account]) { + ds.roles[role][account] = true; + emit RoleGranted(role, account, msg.sender); + + // If granting admin role, also grant operator and investor roles + if (role == DEFAULT_ADMIN_ROLE) { + if (!ds.roles[OPERATOR_ROLE][account]) { + ds.roles[OPERATOR_ROLE][account] = true; + emit RoleGranted(OPERATOR_ROLE, account, msg.sender); + } + if (!ds.roles[INVESTOR_ROLE][account]) { + ds.roles[INVESTOR_ROLE][account] = true; + emit RoleGranted(INVESTOR_ROLE, account, msg.sender); + } + } + } + } + + /// @dev Override _revokeRole to use diamond storage + function _revokeRole(bytes32 role, address account) internal virtual override { + Storage storage ds = StorageLib.get(); + if (ds.roles[role][account]) { + ds.roles[role][account] = false; + emit RoleRevoked(role, account, msg.sender); + } + } + + /// @dev Override _setRoleAdmin to use diamond storage + function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual override { + Storage storage ds = StorageLib.get(); + bytes32 previousAdminRole = ds.roleAdmin[role]; + ds.roleAdmin[role] = adminRole; + emit RoleAdminChanged(role, previousAdminRole, adminRole); + } + + /// @notice Initiates transfer of admin role to a new account + /// @dev Only current admin can initiate transfer + function transferAdmin(address newAdmin) public virtual { + Storage storage ds = StorageLib.get(); + + // Check zero address first + if (newAdmin == address(0)) { + revert AccessControlInvalidTransfer(); + } + + // Then check admin rights + if (msg.sender != ds.currentAdmin) { + revert AccessControlUnauthorized(msg.sender, DEFAULT_ADMIN_ROLE); + } + + // Grant new admin the default admin role + _grantRole(DEFAULT_ADMIN_ROLE, newAdmin); + + ds.pendingAdmin = newAdmin; + } + + /// @notice Accepts admin role transfer + /// @dev Must be called by the pending admin + function acceptAdmin() public virtual { + Storage storage ds = StorageLib.get(); + if (msg.sender != ds.pendingAdmin) { + revert AccessControlInvalidTransfer(); + } + + address oldAdmin = ds.currentAdmin; + + // Grant role to new admin first + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + ds.currentAdmin = msg.sender; + + // Revoke from old admin + _revokeRole(DEFAULT_ADMIN_ROLE, oldAdmin); + + // Clear pending state + ds.pendingAdmin = address(0); + } + + /// @notice Returns the current admin address + /// @return The address of the current admin + function getAdmin() public view returns (address) { + return StorageLib.get().currentAdmin; + } + + /// @notice Returns the pending admin address + /// @return The address of the pending admin + function getPendingAdmin() public view returns (address) { + return StorageLib.get().pendingAdmin; + } +} diff --git a/chain/src/facets/ConvertiblesFacet.sol b/chain/src/facets/ConvertiblesFacet.sol index 02245f3f..fecd8856 100644 --- a/chain/src/facets/ConvertiblesFacet.sol +++ b/chain/src/facets/ConvertiblesFacet.sol @@ -2,36 +2,58 @@ pragma solidity ^0.8.0; import { StorageLib, Storage } from "@core/Storage.sol"; -import { ConvertibleActivePosition } from "@libraries/Structs.sol"; +import { ConvertibleActivePosition, IssueConvertibleParams } from "@libraries/Structs.sol"; import { TxHelper, TxType } from "@libraries/TxHelper.sol"; import { ValidationLib } from "@libraries/ValidationLib.sol"; +import { AccessControl } from "@libraries/AccessControl.sol"; +import { IConvertiblesFacet } from "@interfaces/IConvertiblesFacet.sol"; -contract ConvertiblesFacet { - function issueConvertible(bytes16 stakeholder_id, uint256 investment_amount, bytes16 security_id) external { +contract ConvertiblesFacet is IConvertiblesFacet { + function issueConvertible(IssueConvertibleParams calldata params) external { Storage storage ds = StorageLib.get(); - ValidationLib.validateStakeholder(stakeholder_id); - ValidationLib.validateAmount(investment_amount); + if (!AccessControl.hasOperatorRole(msg.sender)) { + revert AccessControl.AccessControlUnauthorized(msg.sender, AccessControl.OPERATOR_ROLE); + } + + ValidationLib.validateStakeholder(params.stakeholder_id); + ValidationLib.validateAmount(params.investment_amount); // Create and store position - ds.convertibleActivePositions.securities[security_id] = ConvertibleActivePosition({ - stakeholder_id: stakeholder_id, - investment_amount: investment_amount + ds.convertibleActivePositions.securities[params.security_id] = ConvertibleActivePosition({ + stakeholder_id: params.stakeholder_id, + investment_amount: params.investment_amount }); // Track security IDs for this stakeholder - ds.convertibleActivePositions.stakeholderToSecurities[stakeholder_id].push(security_id); + ds.convertibleActivePositions.stakeholderToSecurities[params.stakeholder_id].push(params.security_id); // Add reverse mapping - ds.convertibleActivePositions.securityToStakeholder[security_id] = stakeholder_id; + ds.convertibleActivePositions.securityToStakeholder[params.security_id] = params.stakeholder_id; - // Store transaction - bytes memory txData = abi.encode(stakeholder_id, investment_amount, security_id); + // Emit transaction + bytes memory txData = abi.encode(params); TxHelper.createTx(TxType.CONVERTIBLE_ISSUANCE, txData); } + /// @notice Get details of a convertible position + /// @dev Only OPERATOR_ROLE or the stakeholder who owns the position can view it function getConvertiblePosition(bytes16 securityId) external view returns (ConvertibleActivePosition memory) { Storage storage ds = StorageLib.get(); - return ds.convertibleActivePositions.securities[securityId]; + + ConvertibleActivePosition memory position = ds.convertibleActivePositions.securities[securityId]; + + // Allow operators and admins to view any position + if (AccessControl.hasOperatorRole(msg.sender) || AccessControl.hasAdminRole(msg.sender)) { + return position; + } + + // Otherwise, verify caller is the stakeholder who owns this position + bytes16 stakeholderId = ds.addressToStakeholderId[msg.sender]; + if (stakeholderId != position.stakeholder_id) { + revert AccessControl.AccessControlUnauthorizedOrInvestor(msg.sender); + } + + return position; } } diff --git a/chain/src/facets/EquityCompensationFacet.sol b/chain/src/facets/EquityCompensationFacet.sol index b1148df6..500cb9a7 100644 --- a/chain/src/facets/EquityCompensationFacet.sol +++ b/chain/src/facets/EquityCompensationFacet.sol @@ -2,49 +2,70 @@ pragma solidity ^0.8.0; import { StorageLib, Storage } from "@core/Storage.sol"; -import { EquityCompensationActivePosition, StockActivePosition } from "@libraries/Structs.sol"; +import { + EquityCompensationActivePosition, + StockActivePosition, + IssueEquityCompensationParams +} from "@libraries/Structs.sol"; import { TxHelper, TxType } from "@libraries/TxHelper.sol"; import { ValidationLib } from "@libraries/ValidationLib.sol"; +import { AccessControl } from "@libraries/AccessControl.sol"; +import { IEquityCompensationFacet } from "@interfaces/IEquityCompensationFacet.sol"; -contract EquityCompensationFacet { - function issueEquityCompensation( - bytes16 stakeholder_id, - bytes16 stock_class_id, - bytes16 stock_plan_id, - uint256 quantity, - bytes16 security_id - ) external { +contract EquityCompensationFacet is IEquityCompensationFacet { + /// @notice Issue equity compensation to a stakeholder + /// @dev Only OPERATOR_ROLE can issue equity compensation + function issueEquityCompensation(IssueEquityCompensationParams calldata params) external { Storage storage ds = StorageLib.get(); - ValidationLib.validateStakeholder(stakeholder_id); - ValidationLib.validateStockClass(stock_class_id); - ValidationLib.validateQuantity(quantity); + if (!AccessControl.hasOperatorRole(msg.sender)) { + revert AccessControl.AccessControlUnauthorized(msg.sender, AccessControl.OPERATOR_ROLE); + } + + ValidationLib.validateStakeholder(params.stakeholder_id); + ValidationLib.validateStockClass(params.stock_class_id); + ValidationLib.validateQuantity(params.quantity); // Create and store position - ds.equityCompensationActivePositions.securities[security_id] = EquityCompensationActivePosition({ - stakeholder_id: stakeholder_id, - quantity: quantity, + ds.equityCompensationActivePositions.securities[params.security_id] = EquityCompensationActivePosition({ + stakeholder_id: params.stakeholder_id, + quantity: params.quantity, timestamp: uint40(block.timestamp), - stock_class_id: stock_class_id, - stock_plan_id: stock_plan_id + stock_class_id: params.stock_class_id, + stock_plan_id: params.stock_plan_id }); // Track security IDs for this stakeholder - ds.equityCompensationActivePositions.stakeholderToSecurities[stakeholder_id].push(security_id); + ds.equityCompensationActivePositions.stakeholderToSecurities[params.stakeholder_id].push(params.security_id); // Add reverse mapping - ds.equityCompensationActivePositions.securityToStakeholder[security_id] = stakeholder_id; + ds.equityCompensationActivePositions.securityToStakeholder[params.security_id] = params.stakeholder_id; // Store transaction - bytes memory txData = abi.encode(stakeholder_id, stock_class_id, stock_plan_id, quantity, security_id); + bytes memory txData = abi.encode(params); TxHelper.createTx(TxType.EQUITY_COMPENSATION_ISSUANCE, txData); } - function exerciseEquityCompensation(bytes16 equity_comp_security_id, bytes16 resulting_stock_security_id, uint256 quantity) external { + /// @notice Exercise equity compensation to convert it into stock + /// @dev Only OPERATOR_ROLE can exercise equity compensation + function exerciseEquityCompensation( + bytes16 id, + bytes16 equity_comp_security_id, + bytes16 resulting_stock_security_id, + uint256 quantity + ) + external + { Storage storage ds = StorageLib.get(); + // Check that caller is an operator + if (!AccessControl.hasOperatorRole(msg.sender)) { + revert AccessControl.AccessControlUnauthorized(msg.sender, AccessControl.OPERATOR_ROLE); + } + // Validate equity compensation security exists and has sufficient quantity - EquityCompensationActivePosition memory equityPosition = ds.equityCompensationActivePositions.securities[equity_comp_security_id]; + EquityCompensationActivePosition memory equityPosition = + ds.equityCompensationActivePositions.securities[equity_comp_security_id]; if (quantity == 0) { revert ValidationLib.InvalidQuantity(); @@ -77,8 +98,9 @@ contract EquityCompensationFacet { delete ds.equityCompensationActivePositions.securityToStakeholder[equity_comp_security_id]; // Find and remove the security ID from stakeholder's list - bytes16[] storage stakeholderSecurities = ds.equityCompensationActivePositions.stakeholderToSecurities[equityPosition.stakeholder_id]; - for (uint i = 0; i < stakeholderSecurities.length; i++) { + bytes16[] storage stakeholderSecurities = + ds.equityCompensationActivePositions.stakeholderToSecurities[equityPosition.stakeholder_id]; + for (uint256 i = 0; i < stakeholderSecurities.length; i++) { if (stakeholderSecurities[i] == equity_comp_security_id) { stakeholderSecurities[i] = stakeholderSecurities[stakeholderSecurities.length - 1]; stakeholderSecurities.pop(); @@ -91,12 +113,28 @@ contract EquityCompensationFacet { } // Emit transaction - bytes memory txData = abi.encode(equity_comp_security_id, resulting_stock_security_id, quantity); + bytes memory txData = abi.encode(id, equity_comp_security_id, resulting_stock_security_id, quantity); TxHelper.createTx(TxType.EQUITY_COMPENSATION_EXERCISE, txData); } + /// @notice Get details of an equity compensation position + /// @dev Only OPERATOR_ROLE or the stakeholder who owns the position can view it function getPosition(bytes16 securityId) external view returns (EquityCompensationActivePosition memory) { Storage storage ds = StorageLib.get(); - return ds.equityCompensationActivePositions.securities[securityId]; + + EquityCompensationActivePosition memory position = ds.equityCompensationActivePositions.securities[securityId]; + + // Allow operators and admins to view any position + if (AccessControl.hasOperatorRole(msg.sender) || AccessControl.hasAdminRole(msg.sender)) { + return position; + } + + // Otherwise, verify caller is the stakeholder who owns this position + bytes16 stakeholderId = ds.addressToStakeholderId[msg.sender]; + if (stakeholderId != position.stakeholder_id) { + revert AccessControl.AccessControlUnauthorizedOrInvestor(msg.sender); + } + + return position; } } diff --git a/chain/src/facets/IssuerFacet.sol b/chain/src/facets/IssuerFacet.sol index e3499e36..2aea3c26 100644 --- a/chain/src/facets/IssuerFacet.sol +++ b/chain/src/facets/IssuerFacet.sol @@ -5,6 +5,8 @@ import { LibDiamond } from "diamond-3-hardhat/libraries/LibDiamond.sol"; import { StorageLib, Storage } from "@core/Storage.sol"; import { Issuer } from "@libraries/Structs.sol"; import { TxHelper, TxType } from "@libraries/TxHelper.sol"; +import { AccessControl } from "@libraries/AccessControl.sol"; +import { console } from "forge-std/console.sol"; contract IssuerFacet { error IssuerAlreadyInitialized(); @@ -12,9 +14,16 @@ contract IssuerFacet { event IssuerAuthorizedSharesAdjusted(uint256 newSharesAuthorized); + /// @notice Initialize the issuer with initial shares authorized + /// @dev Can only be called once by an admin during setup function initializeIssuer(bytes16 id, uint256 initial_shares_authorized) external { Storage storage ds = StorageLib.get(); + // Check that caller has admin role + if (!AccessControl.hasAdminRole(msg.sender)) { + revert AccessControl.AccessControlUnauthorized(msg.sender, AccessControl.DEFAULT_ADMIN_ROLE); + } + if (ds.issuer.shares_authorized != 0) { revert IssuerAlreadyInitialized(); } @@ -22,12 +31,21 @@ contract IssuerFacet { ds.issuer = Issuer({ id: id, shares_issued: 0, shares_authorized: initial_shares_authorized }); } - function adjustIssuerAuthorizedShares(uint256 newSharesAuthorized) external { + /// @notice Adjust the total number of authorized shares for the issuer + /// @dev Only DEFAULT_ADMIN_ROLE can adjust authorized shares + function adjustIssuerAuthorizedShares(bytes16 id, uint256 newSharesAuthorized) external { Storage storage ds = StorageLib.get(); + // Check that caller has admin role + if (!AccessControl.hasAdminRole(msg.sender)) { + revert AccessControl.AccessControlUnauthorized(msg.sender, AccessControl.DEFAULT_ADMIN_ROLE); + } + + // Check that new shares authorized is not less than current shares issued + require(newSharesAuthorized >= ds.issuer.shares_issued, "New shares authorized must be >= shares issued"); + ds.issuer.shares_authorized = newSharesAuthorized; - emit IssuerAuthorizedSharesAdjusted(newSharesAuthorized); - TxHelper.createTx(TxType.ISSUER_AUTHORIZED_SHARES_ADJUSTMENT, abi.encode(newSharesAuthorized)); + TxHelper.createTx(TxType.ISSUER_AUTHORIZED_SHARES_ADJUSTMENT, abi.encode(id, ds.issuer.id, newSharesAuthorized)); } } diff --git a/chain/src/facets/StakeholderFacet.sol b/chain/src/facets/StakeholderFacet.sol index 512be97d..0a1fbf5c 100644 --- a/chain/src/facets/StakeholderFacet.sol +++ b/chain/src/facets/StakeholderFacet.sol @@ -2,7 +2,14 @@ pragma solidity ^0.8.0; import { StorageLib, Storage } from "@core/Storage.sol"; -import { StockActivePosition, WarrantActivePosition, ConvertibleActivePosition, EquityCompensationActivePosition, StakeholderPositions } from "@libraries/Structs.sol"; +import { + StockActivePosition, + WarrantActivePosition, + ConvertibleActivePosition, + EquityCompensationActivePosition, + StakeholderPositions +} from "@libraries/Structs.sol"; +import { AccessControl } from "@libraries/AccessControl.sol"; contract StakeholderFacet { event StakeholderCreated(bytes16 indexed id); @@ -11,9 +18,15 @@ contract StakeholderFacet { error StakeholderAlreadyExists(bytes16 stakeholder_id); error AddressAlreadyLinked(address wallet_address); + /// @notice Create a new stakeholder + /// @dev Only OPERATOR_ROLE can create stakeholders function createStakeholder(bytes16 _id) external { Storage storage ds = StorageLib.get(); + if (!AccessControl.hasOperatorRole(msg.sender)) { + revert AccessControl.AccessControlUnauthorized(msg.sender, AccessControl.OPERATOR_ROLE); + } + if (ds.stakeholderIndex[_id] > 0) { revert StakeholderAlreadyExists(_id); } @@ -24,9 +37,15 @@ contract StakeholderFacet { emit StakeholderCreated(_id); } + /// @notice Link a wallet address to a stakeholder + /// @dev Only OPERATOR_ROLE can link addresses function linkStakeholderAddress(bytes16 stakeholder_id, address wallet_address) external { Storage storage ds = StorageLib.get(); + if (!AccessControl.hasOperatorRole(msg.sender)) { + revert AccessControl.AccessControlUnauthorized(msg.sender, AccessControl.OPERATOR_ROLE); + } + // Check if address is already linked if (ds.addressToStakeholderId[wallet_address] != bytes16(0)) { revert AddressAlreadyLinked(wallet_address); @@ -38,36 +57,55 @@ contract StakeholderFacet { emit StakeholderAddressLinked(stakeholder_id, wallet_address); } + /// @notice Get all positions for a stakeholder + /// @dev INVESTOR_ROLE can only view their own positions, OPERATOR_ROLE and above can view any function getStakeholderPositions(bytes16 stakeholder_id) external view returns (StakeholderPositions memory) { Storage storage ds = StorageLib.get(); + // Check that caller has at least investor role + if ( + !AccessControl.hasAdminRole(msg.sender) && !AccessControl.hasOperatorRole(msg.sender) + && !AccessControl.hasInvestorRole(msg.sender) + ) { + revert AccessControl.AccessControlUnauthorizedOrInvestor(msg.sender); + } + + // If caller is an investor, they can only view their own positions + if ( + AccessControl.hasInvestorRole(msg.sender) && !AccessControl.hasOperatorRole(msg.sender) + && !AccessControl.hasAdminRole(msg.sender) + ) { + require(ds.addressToStakeholderId[msg.sender] == stakeholder_id, "Can only view own positions"); + } + StakeholderPositions memory positions; // Populate stocks bytes16[] storage stockSecurities = ds.stockActivePositions.stakeholderToSecurities[stakeholder_id]; positions.stocks = new StockActivePosition[](stockSecurities.length); - for (uint i = 0; i < stockSecurities.length; i++) { + for (uint256 i = 0; i < stockSecurities.length; i++) { positions.stocks[i] = ds.stockActivePositions.securities[stockSecurities[i]]; } // Populate warrants bytes16[] storage warrantSecurities = ds.warrantActivePositions.stakeholderToSecurities[stakeholder_id]; positions.warrants = new WarrantActivePosition[](warrantSecurities.length); - for (uint i = 0; i < warrantSecurities.length; i++) { + for (uint256 i = 0; i < warrantSecurities.length; i++) { positions.warrants[i] = ds.warrantActivePositions.securities[warrantSecurities[i]]; } // Populate convertibles bytes16[] storage convertibleSecurities = ds.convertibleActivePositions.stakeholderToSecurities[stakeholder_id]; positions.convertibles = new ConvertibleActivePosition[](convertibleSecurities.length); - for (uint i = 0; i < convertibleSecurities.length; i++) { + for (uint256 i = 0; i < convertibleSecurities.length; i++) { positions.convertibles[i] = ds.convertibleActivePositions.securities[convertibleSecurities[i]]; } // Populate equity compensations - bytes16[] storage equityCompSecurities = ds.equityCompensationActivePositions.stakeholderToSecurities[stakeholder_id]; + bytes16[] storage equityCompSecurities = + ds.equityCompensationActivePositions.stakeholderToSecurities[stakeholder_id]; positions.equityCompensations = new EquityCompensationActivePosition[](equityCompSecurities.length); - for (uint i = 0; i < equityCompSecurities.length; i++) { + for (uint256 i = 0; i < equityCompSecurities.length; i++) { positions.equityCompensations[i] = ds.equityCompensationActivePositions.securities[equityCompSecurities[i]]; } diff --git a/chain/src/facets/StakeholderNFTFacet.sol b/chain/src/facets/StakeholderNFTFacet.sol index cc887abf..f30e2cab 100644 --- a/chain/src/facets/StakeholderNFTFacet.sol +++ b/chain/src/facets/StakeholderNFTFacet.sol @@ -4,21 +4,35 @@ pragma solidity ^0.8.0; import "openzeppelin-contracts/contracts/token/ERC721/ERC721.sol"; import "openzeppelin-contracts/contracts/utils/Base64.sol"; import { StorageLib, Storage } from "@core/Storage.sol"; -import { StakeholderPositions, StockActivePosition, WarrantActivePosition, ConvertibleActivePosition, EquityCompensationActivePosition } from "@libraries/Structs.sol"; +import { + StakeholderPositions, + StockActivePosition, + WarrantActivePosition, + ConvertibleActivePosition, + EquityCompensationActivePosition +} from "@libraries/Structs.sol"; import { ValidationLib } from "@libraries/ValidationLib.sol"; import { StakeholderFacet } from "@facets/StakeholderFacet.sol"; +import { AccessControl } from "@libraries/AccessControl.sol"; contract StakeholderNFTFacet is ERC721 { error NotStakeholder(); error AlreadyMinted(); error URIQueryForNonexistentToken(); - constructor() ERC721("Stakeholder Position", "STKPOS") {} + constructor() ERC721("Stakeholder Position", "STKPOS") { } + /// @notice Mint an NFT representing a stakeholder's position + /// @dev Only stakeholders with INVESTOR_ROLE can mint their own NFT function mint() external { Storage storage ds = StorageLib.get(); - // Get stakeholder ID from msg.sender (we'll need to add a mapping for this) + // Verify caller has investor role + if (!AccessControl.hasInvestorRole(msg.sender)) { + revert AccessControl.AccessControlUnauthorized(msg.sender, AccessControl.INVESTOR_ROLE); + } + + // Get stakeholder ID from msg.sender bytes16 stakeholderId = ds.addressToStakeholderId[msg.sender]; if (ds.stakeholderIndex[stakeholderId] == 0) { @@ -34,73 +48,97 @@ contract StakeholderNFTFacet is ERC721 { _mint(msg.sender, tokenId); } + /// @notice Get the URI for a token, containing metadata about stakeholder positions + /// @dev Only OPERATOR_ROLE or the token owner can view the token URI function tokenURI(uint256 tokenId) public view override returns (string memory) { if (!_exists(tokenId)) revert URIQueryForNonexistentToken(); + // Allow operators and admins to view any token URI + if (!AccessControl.hasOperatorRole(msg.sender) && !AccessControl.hasAdminRole(msg.sender)) { + // For non-operators, verify caller is the token owner (investor) + if (ownerOf(tokenId) != msg.sender) { + revert AccessControl.AccessControlUnauthorizedOrInvestor(msg.sender); + } + } + bytes16 stakeholderId = bytes16(uint128(tokenId)); StakeholderPositions memory positions = StakeholderFacet(address(this)).getStakeholderPositions(stakeholderId); - return - string( - abi.encodePacked( - "data:application/json;base64,", - Base64.encode( - bytes( - abi.encodePacked( - '{"name":"Stakeholder Position #', - toString(tokenId), - '","description":"This NFT represents all active positions for this stakeholder.",', - '"attributes":', - _getAttributesJson(positions), - "}" - ) + return string( + abi.encodePacked( + "data:application/json;base64,", + Base64.encode( + bytes( + abi.encodePacked( + '{"name":"Stakeholder Position #', + toString(tokenId), + '","description":"This NFT represents all active positions for this stakeholder.",', + '"attributes":', + _getAttributesJson(positions), + "}" ) ) ) - ); + ) + ); } function _getAttributesJson(StakeholderPositions memory positions) internal pure returns (string memory) { // Convert positions to JSON format - return - string( - abi.encodePacked( - "[", - _getStockPositionsJson(positions.stocks), - ",", - _getWarrantPositionsJson(positions.warrants), - ",", - _getConvertiblePositionsJson(positions.convertibles), - ",", - _getEquityCompPositionsJson(positions.equityCompensations), - "]" - ) - ); + return string( + abi.encodePacked( + "[", + _getStockPositionsJson(positions.stocks), + ",", + _getWarrantPositionsJson(positions.warrants), + ",", + _getConvertiblePositionsJson(positions.convertibles), + ",", + _getEquityCompPositionsJson(positions.equityCompensations), + "]" + ) + ); } // Helper functions for JSON conversion function _getStockPositionsJson(StockActivePosition[] memory positions) internal pure returns (string memory) { if (positions.length == 0) return '{"trait_type": "Stock Positions", "value": "0"}'; - return string(abi.encodePacked('{"trait_type": "Stock Positions", "value": "', toString(positions.length), '"}')); + return + string(abi.encodePacked('{"trait_type": "Stock Positions", "value": "', toString(positions.length), '"}')); } function _getWarrantPositionsJson(WarrantActivePosition[] memory positions) internal pure returns (string memory) { if (positions.length == 0) return '{"trait_type": "Warrant Positions", "value": "0"}'; - return string(abi.encodePacked('{"trait_type": "Warrant Positions", "value": "', toString(positions.length), '"}')); + return + string(abi.encodePacked('{"trait_type": "Warrant Positions", "value": "', toString(positions.length), '"}')); } - function _getConvertiblePositionsJson(ConvertibleActivePosition[] memory positions) internal pure returns (string memory) { + function _getConvertiblePositionsJson(ConvertibleActivePosition[] memory positions) + internal + pure + returns (string memory) + { if (positions.length == 0) return '{"trait_type": "Convertible Positions", "value": "0"}'; - return string(abi.encodePacked('{"trait_type": "Convertible Positions", "value": "', toString(positions.length), '"}')); + return string( + abi.encodePacked('{"trait_type": "Convertible Positions", "value": "', toString(positions.length), '"}') + ); } - function _getEquityCompPositionsJson(EquityCompensationActivePosition[] memory positions) internal pure returns (string memory) { + function _getEquityCompPositionsJson(EquityCompensationActivePosition[] memory positions) + internal + pure + returns (string memory) + { if (positions.length == 0) return '{"trait_type": "Equity Compensation Positions", "value": "0"}'; - return string(abi.encodePacked('{"trait_type": "Equity Compensation Positions", "value": "', toString(positions.length), '"}')); + return string( + abi.encodePacked( + '{"trait_type": "Equity Compensation Positions", "value": "', toString(positions.length), '"}' + ) + ); } function toString(uint256 value) internal pure returns (string memory) { diff --git a/chain/src/facets/StockClassFacet.sol b/chain/src/facets/StockClassFacet.sol index eddced1a..58fc3001 100644 --- a/chain/src/facets/StockClassFacet.sol +++ b/chain/src/facets/StockClassFacet.sol @@ -5,22 +5,42 @@ import { StorageLib, Storage } from "@core/Storage.sol"; import { StockClass } from "@libraries/Structs.sol"; import { TxHelper, TxType } from "@libraries/TxHelper.sol"; import { LibDiamond } from "diamond-3-hardhat/libraries/LibDiamond.sol"; +import { AccessControl } from "@libraries/AccessControl.sol"; contract StockClassFacet { - event StockClassCreated(bytes16 indexed id, string indexed classType, uint256 indexed pricePerShare, uint256 initialSharesAuthorized); + event StockClassCreated( + bytes16 indexed id, string indexed classType, uint256 indexed pricePerShare, uint256 initialSharesAuthorized + ); event StockClassAuthorizedSharesAdjusted(bytes16 indexed stockClassId, uint256 newSharesAuthorized); error StockClassAlreadyExists(bytes16 stock_class_id); error StockClassNotFound(bytes16 stock_class_id); error InvalidSharesAuthorized(); - function createStockClass(bytes16 _id, string memory _class_type, uint256 _price_per_share, uint256 _initial_share_authorized) external { + /// @notice Create a new stock class + /// @dev Only DEFAULT_ADMIN_ROLE can create stock classes + function createStockClass( + bytes16 _id, + string memory _class_type, + uint256 _price_per_share, + uint256 _initial_share_authorized + ) + external + { Storage storage ds = StorageLib.get(); + // Check that caller has admin role + if (!AccessControl.hasAdminRole(msg.sender)) { + revert AccessControl.AccessControlUnauthorized(msg.sender, AccessControl.DEFAULT_ADMIN_ROLE); + } + if (ds.stockClassIndex[_id] > 0) { revert StockClassAlreadyExists(_id); } + // Check that initial shares authorized don't exceed issuer's total authorized shares + require(_initial_share_authorized <= ds.issuer.shares_authorized, "Exceeds issuer authorized shares"); + ds.stockClasses.push( StockClass({ id: _id, @@ -36,8 +56,16 @@ contract StockClassFacet { emit StockClassCreated(_id, _class_type, _price_per_share, _initial_share_authorized); } - function adjustAuthorizedShares(bytes16 stockClassId, uint256 newSharesAuthorized) external { + /// @notice Adjust the authorized shares for a stock class + /// @dev Only DEFAULT_ADMIN_ROLE can adjust authorized shares + function adjustAuthorizedShares(bytes16 id, bytes16 stockClassId, uint256 newSharesAuthorized) external { Storage storage ds = StorageLib.get(); + + // Check that caller has admin role + if (!AccessControl.hasAdminRole(msg.sender)) { + revert AccessControl.AccessControlUnauthorized(msg.sender, AccessControl.DEFAULT_ADMIN_ROLE); + } + uint256 stockClassIndex = ds.stockClassIndex[stockClassId]; if (stockClassIndex == 0) { @@ -45,9 +73,17 @@ contract StockClassFacet { } StockClass storage stockClass = ds.stockClasses[stockClassIndex - 1]; + + // Check that new shares authorized don't exceed issuer's total authorized shares + require(newSharesAuthorized <= ds.issuer.shares_authorized, "Exceeds issuer authorized shares"); + // Check that new shares authorized is not less than current shares issued + require(newSharesAuthorized >= stockClass.shares_issued, "New shares authorized must be >= shares issued"); + stockClass.shares_authorized = newSharesAuthorized; emit StockClassAuthorizedSharesAdjusted(stockClassId, newSharesAuthorized); - TxHelper.createTx(TxType.STOCK_CLASS_AUTHORIZED_SHARES_ADJUSTMENT, abi.encode(newSharesAuthorized)); + TxHelper.createTx( + TxType.STOCK_CLASS_AUTHORIZED_SHARES_ADJUSTMENT, abi.encode(id, stockClassId, newSharesAuthorized) + ); } } diff --git a/chain/src/facets/StockFacet.sol b/chain/src/facets/StockFacet.sol index 2c7cb9b0..3957c961 100644 --- a/chain/src/facets/StockFacet.sol +++ b/chain/src/facets/StockFacet.sol @@ -2,49 +2,342 @@ pragma solidity ^0.8.0; import { StorageLib, Storage } from "@core/Storage.sol"; -import { StockActivePosition, StockClass } from "@libraries/Structs.sol"; +import { + StockActivePosition, + StockClass, + IssueStockParams, + StockActivePositions, + StockConsolidationTx, + StockTransferTx +} from "@libraries/Structs.sol"; import { TxHelper, TxType } from "@libraries/TxHelper.sol"; import { ValidationLib } from "@libraries/ValidationLib.sol"; +import { AccessControl } from "@libraries/AccessControl.sol"; contract StockFacet { - function issueStock(bytes16 stock_class_id, uint256 share_price, uint256 quantity, bytes16 stakeholder_id, bytes16 security_id) external { + /// @notice Issue new stock to a stakeholder + /// @dev Only OPERATOR_ROLE can issue stock + error InvalidSecurityId(bytes16 security_id); + + /// @dev Add these custom errors at the contract level + error NoPositionsToConsolidate(); + error StockClassMismatch(bytes16 expected, bytes16 actual); + error ZeroQuantityPosition(bytes16 security_id); + + function issueStock(IssueStockParams calldata params) external { Storage storage ds = StorageLib.get(); - ValidationLib.validateStakeholder(stakeholder_id); - ValidationLib.validateStockClass(stock_class_id); - ValidationLib.validateQuantity(quantity); - ValidationLib.validateAmount(share_price); - ValidationLib.validateSharesAvailable(stock_class_id, quantity); + if (!AccessControl.hasOperatorRole(msg.sender)) { + revert AccessControl.AccessControlUnauthorized(msg.sender, AccessControl.OPERATOR_ROLE); + } + + ValidationLib.validateStakeholder(params.stakeholder_id); + ValidationLib.validateStockClass(params.stock_class_id); + ValidationLib.validateQuantity(params.quantity); + ValidationLib.validateAmount(params.share_price); + ValidationLib.validateSharesAvailable(params.stock_class_id, params.quantity); // Get stock class for share tracking - uint256 stockClassIdx = ds.stockClassIndex[stock_class_id] - 1; + uint256 stockClassIdx = ds.stockClassIndex[params.stock_class_id] - 1; StockClass storage stockClass = ds.stockClasses[stockClassIdx]; // Create and store position - ds.stockActivePositions.securities[security_id] = StockActivePosition({ - stakeholder_id: stakeholder_id, - stock_class_id: stock_class_id, - quantity: quantity, - share_price: share_price + ds.stockActivePositions.securities[params.security_id] = StockActivePosition({ + stakeholder_id: params.stakeholder_id, + stock_class_id: params.stock_class_id, + quantity: params.quantity, + share_price: params.share_price }); // Track security IDs for this stakeholder - ds.stockActivePositions.stakeholderToSecurities[stakeholder_id].push(security_id); + ds.stockActivePositions.stakeholderToSecurities[params.stakeholder_id].push(params.security_id); // Add reverse mapping - ds.stockActivePositions.securityToStakeholder[security_id] = stakeholder_id; + ds.stockActivePositions.securityToStakeholder[params.security_id] = params.stakeholder_id; // Update share counts - stockClass.shares_issued += quantity; - ds.issuer.shares_issued += quantity; + stockClass.shares_issued += params.quantity; + ds.issuer.shares_issued += params.quantity; - // Store transaction - Match test order: stockClassId, sharePrice, quantity, stakeholderId, securityId - bytes memory txData = abi.encode(stock_class_id, share_price, quantity, stakeholder_id, security_id); + // Store transaction - Include mapping fields in transaction data + bytes memory txData = abi.encode(params); TxHelper.createTx(TxType.STOCK_ISSUANCE, txData); } + /// @notice Get details of a stock position + /// @dev Accessible to INVESTOR_ROLE and above function getStockPosition(bytes16 securityId) external view returns (StockActivePosition memory) { Storage storage ds = StorageLib.get(); + + // Check that caller has at least investor role + if ( + !AccessControl.hasAdminRole(msg.sender) && !AccessControl.hasOperatorRole(msg.sender) + && !AccessControl.hasInvestorRole(msg.sender) + ) { + revert AccessControl.AccessControlUnauthorizedOrInvestor(msg.sender); + } + + // If caller is an investor, they can only view their own positions + if ( + AccessControl.hasInvestorRole(msg.sender) && !AccessControl.hasOperatorRole(msg.sender) + && !AccessControl.hasAdminRole(msg.sender) + ) { + bytes16 stakeholderId = ds.stockActivePositions.securityToStakeholder[securityId]; + require(ds.addressToStakeholderId[msg.sender] == stakeholderId, "Can only view own positions"); + } + return ds.stockActivePositions.securities[securityId]; } + + /// @dev Private helper to get stakeholder securities + function _getStakeholderSecurities( + bytes16 stakeholder_id, + bytes16 stock_class_id + ) + private + view + returns (bytes16[] memory) + { + Storage storage ds = StorageLib.get(); + bytes16[] storage allSecurities = ds.stockActivePositions.stakeholderToSecurities[stakeholder_id]; + + // First count matching securities + uint256 matchCount = 0; + for (uint256 i = 0; i < allSecurities.length; i++) { + if (ds.stockActivePositions.securities[allSecurities[i]].stock_class_id == stock_class_id) { + matchCount++; + } + } + + // Create array of matching securities + bytes16[] memory matchingSecurities = new bytes16[](matchCount); + uint256 matchIndex = 0; + for (uint256 i = 0; i < allSecurities.length; i++) { + if (ds.stockActivePositions.securities[allSecurities[i]].stock_class_id == stock_class_id) { + matchingSecurities[matchIndex] = allSecurities[i]; + matchIndex++; + } + } + + return matchingSecurities; + } + + /// @notice Get all security IDs for a stakeholder of a specific stock class + /// @dev Accessible to INVESTOR_ROLE and above. Investors can only view their own positions + function getStakeholderSecurities( + bytes16 stakeholder_id, + bytes16 stock_class_id + ) + external + view + returns (bytes16[] memory) + { + Storage storage ds = StorageLib.get(); + + // Check that caller has at least investor role + if ( + !AccessControl.hasAdminRole(msg.sender) && !AccessControl.hasOperatorRole(msg.sender) + && !AccessControl.hasInvestorRole(msg.sender) + ) { + revert AccessControl.AccessControlUnauthorizedOrInvestor(msg.sender); + } + + // If caller is an investor, they can only view their own positions + if ( + AccessControl.hasInvestorRole(msg.sender) && !AccessControl.hasOperatorRole(msg.sender) + && !AccessControl.hasAdminRole(msg.sender) + ) { + require(ds.addressToStakeholderId[msg.sender] == stakeholder_id, "Can only view own positions"); + } + + return _getStakeholderSecurities(stakeholder_id, stock_class_id); + } + + /// @dev Internal function to consolidate positions before transfer + function _consolidatePositions( + bytes16[] memory security_ids, + bytes16 stakeholder_id, + bytes16 stock_class_id + ) + internal + returns (bytes16) + { + Storage storage ds = StorageLib.get(); + + // Check if there are positions to consolidate + if (security_ids.length == 0) { + revert NoPositionsToConsolidate(); + } + + // Initialize hash with base components + bytes32 running_hash = + keccak256(abi.encodePacked(block.timestamp, stakeholder_id, stock_class_id, "CONSOLIDATION")); + + uint256 total_quantity = 0; + uint256 weighted_share_price = 0; + + // Calculate totals and weighted average price + for (uint256 i = 0; i < security_ids.length; i++) { + bytes16 security_id = security_ids[i]; + StockActivePosition storage position = ds.stockActivePositions.securities[security_id]; + + // Check if position exists and has valid quantity + if (position.quantity == 0) { + revert ZeroQuantityPosition(security_id); + } + + // Validate stock class + if (position.stock_class_id != stock_class_id) { + revert StockClassMismatch(stock_class_id, position.stock_class_id); + } + + // Update running hash with each security ID + running_hash = keccak256(abi.encodePacked(running_hash, security_id)); + + weighted_share_price += position.share_price * position.quantity; + total_quantity += position.quantity; + } + + // Convert final hash to bytes16 for security ID + bytes16 resulting_security_id = bytes16(running_hash); + + // Create consolidated position + ds.stockActivePositions.securities[resulting_security_id] = StockActivePosition({ + stakeholder_id: stakeholder_id, + stock_class_id: stock_class_id, + quantity: total_quantity, + share_price: total_quantity > 0 ? weighted_share_price / total_quantity : 0 + }); + + // Update mappings + ds.stockActivePositions.stakeholderToSecurities[stakeholder_id].push(resulting_security_id); + ds.stockActivePositions.securityToStakeholder[resulting_security_id] = stakeholder_id; + + // Remove old positions + for (uint256 i = 0; i < security_ids.length; i++) { + removeSecurityFromStakeholder(ds.stockActivePositions, stakeholder_id, security_ids[i]); + } + + // Record consolidation transaction + + StockConsolidationTx memory consolidationTx = + StockConsolidationTx({ security_ids: security_ids, resulting_security_id: resulting_security_id }); + bytes memory consolidationData = abi.encode(consolidationTx); + TxHelper.createTx(TxType.STOCK_CONSOLIDATION, consolidationData); + + return resulting_security_id; + } + + /// @dev Helper function to remove a security from a stakeholder's array + function removeSecurityFromStakeholder( + StockActivePositions storage positions, + bytes16 stakeholderId, + bytes16 securityId + ) + internal + { + bytes16[] storage securities = positions.stakeholderToSecurities[stakeholderId]; + for (uint256 i = 0; i < securities.length; i++) { + if (securities[i] == securityId) { + // Move the last element to the position being deleted + securities[i] = securities[securities.length - 1]; + // Remove the last element + securities.pop(); + break; + } + } + // Clean up the security to stakeholder mapping + delete positions.securityToStakeholder[securityId]; + // Clean up the security itself + delete positions.securities[securityId]; + } + + /// @notice Transfer stock from one stakeholder to another + /// @dev Only OPERATOR_ROLE can transfer stock + function transferStock( + bytes16 transferor_stakeholder_id, + bytes16 transferee_stakeholder_id, + bytes16 stock_class_id, + uint256 quantity, + uint256 share_price + ) + external + { + Storage storage ds = StorageLib.get(); + + // Validations + if (!AccessControl.hasOperatorRole(msg.sender)) { + revert AccessControl.AccessControlUnauthorized(msg.sender, AccessControl.OPERATOR_ROLE); + } + + ValidationLib.validateStakeholder(transferor_stakeholder_id); + ValidationLib.validateStakeholder(transferee_stakeholder_id); + ValidationLib.validateStockClass(stock_class_id); + ValidationLib.validateQuantity(quantity); + ValidationLib.validateAmount(share_price); + + // First consolidate transferor's positions + bytes16[] memory security_ids = _getStakeholderSecurities(transferor_stakeholder_id, stock_class_id); + bytes16 consolidated_security_id = + _consolidatePositions(security_ids, transferor_stakeholder_id, stock_class_id); + + // Get consolidated position + StockActivePosition storage consolidated_position = ds.stockActivePositions.securities[consolidated_security_id]; + require(consolidated_position.quantity >= quantity, "Insufficient shares for transfer"); + + // Generate new security IDs + bytes16 transferee_security_id = bytes16( + keccak256( + abi.encodePacked(block.timestamp, consolidated_security_id, transferee_stakeholder_id, "TRANSFER") + ) + ); + bytes16 remainder_security_id; + + // Create transferee position + ds.stockActivePositions.securities[transferee_security_id] = StockActivePosition({ + stakeholder_id: transferee_stakeholder_id, + stock_class_id: stock_class_id, + quantity: quantity, + share_price: share_price + }); + + // Update transferee mappings + ds.stockActivePositions.stakeholderToSecurities[transferee_stakeholder_id].push(transferee_security_id); + ds.stockActivePositions.securityToStakeholder[transferee_security_id] = transferee_stakeholder_id; + + // Handle remainder if partial transfer + if (consolidated_position.quantity > quantity) { + remainder_security_id = bytes16( + keccak256( + abi.encodePacked(block.timestamp, consolidated_security_id, transferor_stakeholder_id, "REMAINDER") + ) + ); + + ds.stockActivePositions.securities[remainder_security_id] = StockActivePosition({ + stakeholder_id: transferor_stakeholder_id, + stock_class_id: stock_class_id, + quantity: consolidated_position.quantity - quantity, + share_price: consolidated_position.share_price // Keep original price for remainder + }); + + // Update transferor mappings for remainder + ds.stockActivePositions.stakeholderToSecurities[transferor_stakeholder_id].push(remainder_security_id); + ds.stockActivePositions.securityToStakeholder[remainder_security_id] = transferor_stakeholder_id; + } + + // Clean up consolidated position + removeSecurityFromStakeholder(ds.stockActivePositions, transferor_stakeholder_id, consolidated_security_id); + + // Record transfer transaction + bytes memory transferData = abi.encode( + StockTransferTx({ + consolidated_security_id: consolidated_security_id, + transferee_security_id: transferee_security_id, + remainder_security_id: remainder_security_id, + quantity: quantity, + share_price: share_price + }) + ); + TxHelper.createTx(TxType.STOCK_TRANSFER, transferData); + } } diff --git a/chain/src/facets/StockPlanFacet.sol b/chain/src/facets/StockPlanFacet.sol index 0fa5777f..ccabf71a 100644 --- a/chain/src/facets/StockPlanFacet.sol +++ b/chain/src/facets/StockPlanFacet.sol @@ -5,6 +5,7 @@ import { StorageLib, Storage } from "@core/Storage.sol"; import { StockPlan } from "@libraries/Structs.sol"; import { LibDiamond } from "diamond-3-hardhat/libraries/LibDiamond.sol"; import { TxHelper, TxType } from "@libraries/TxHelper.sol"; +import { AccessControl } from "@libraries/AccessControl.sol"; contract StockPlanFacet { event StockPlanCreated(bytes16 indexed id, uint256 shares_reserved); @@ -14,28 +15,41 @@ contract StockPlanFacet { error InvalidStockClass(bytes16 stock_class_id); error StockPlanNotFound(bytes16 stock_plan_id); - function createStockPlan(bytes16 _id, bytes16[] memory _stock_class_ids, uint256 _shares_reserved) external { + /// @notice Create a new stock plan with specified stock classes and reserved shares + /// @dev Only OPERATOR_ROLE can create stock plans + function createStockPlan(bytes16 id, bytes16[] memory stock_class_ids, uint256 shares_reserved) external { Storage storage ds = StorageLib.get(); - if (ds.stockPlanIndex[_id] > 0) { - revert StockPlanAlreadyExists(_id); + if (!AccessControl.hasOperatorRole(msg.sender)) { + revert AccessControl.AccessControlUnauthorized(msg.sender, AccessControl.OPERATOR_ROLE); + } + + if (ds.stockPlanIndex[id] > 0) { + revert StockPlanAlreadyExists(id); } // Verify all stock classes exist - for (uint256 i = 0; i < _stock_class_ids.length; i++) { - if (ds.stockClassIndex[_stock_class_ids[i]] == 0) { - revert InvalidStockClass(_stock_class_ids[i]); + for (uint256 i = 0; i < stock_class_ids.length; i++) { + if (ds.stockClassIndex[stock_class_ids[i]] == 0) { + revert InvalidStockClass(stock_class_ids[i]); } } - ds.stockPlans.push(StockPlan({ stock_class_ids: _stock_class_ids, shares_reserved: _shares_reserved })); - ds.stockPlanIndex[_id] = ds.stockPlans.length; + ds.stockPlans.push(StockPlan({ stock_class_ids: stock_class_ids, shares_reserved: shares_reserved })); + ds.stockPlanIndex[id] = ds.stockPlans.length; - emit StockPlanCreated(_id, _shares_reserved); + emit StockPlanCreated(id, shares_reserved); } - function adjustStockPlanPool(bytes16 stockPlanId, uint256 newSharesReserved) external { + /// @notice Adjust the number of shares reserved in a stock plan + /// @dev Only OPERATOR_ROLE can adjust stock plan pools + function adjustStockPlanPool(bytes16 id, bytes16 stockPlanId, uint256 newSharesReserved) external { Storage storage ds = StorageLib.get(); + + if (!AccessControl.hasOperatorRole(msg.sender)) { + revert AccessControl.AccessControlUnauthorized(msg.sender, AccessControl.OPERATOR_ROLE); + } + uint256 stockPlanIndex = ds.stockPlanIndex[stockPlanId]; if (stockPlanIndex == 0) { @@ -45,6 +59,6 @@ contract StockPlanFacet { StockPlan storage stockPlan = ds.stockPlans[stockPlanIndex - 1]; stockPlan.shares_reserved = newSharesReserved; - TxHelper.createTx(TxType.STOCK_PLAN_POOL_ADJUSTMENT, abi.encode(newSharesReserved)); + TxHelper.createTx(TxType.STOCK_PLAN_POOL_ADJUSTMENT, abi.encode(id, stockPlanId, newSharesReserved)); } } diff --git a/chain/src/facets/WarrantFacet.sol b/chain/src/facets/WarrantFacet.sol index aa4201b6..2051221e 100644 --- a/chain/src/facets/WarrantFacet.sol +++ b/chain/src/facets/WarrantFacet.sol @@ -2,33 +2,55 @@ pragma solidity ^0.8.0; import { StorageLib, Storage } from "@core/Storage.sol"; -import { WarrantActivePosition } from "@libraries/Structs.sol"; +import { WarrantActivePosition, IssueWarrantParams } from "@libraries/Structs.sol"; import { TxHelper, TxType } from "@libraries/TxHelper.sol"; import { ValidationLib } from "@libraries/ValidationLib.sol"; +import { AccessControl } from "@libraries/AccessControl.sol"; contract WarrantFacet { - function issueWarrant(bytes16 stakeholder_id, uint256 quantity, bytes16 security_id) external { + function issueWarrant(IssueWarrantParams calldata params) external { Storage storage ds = StorageLib.get(); - ValidationLib.validateStakeholder(stakeholder_id); - ValidationLib.validateQuantity(quantity); + if (!AccessControl.hasOperatorRole(msg.sender)) { + revert AccessControl.AccessControlUnauthorized(msg.sender, AccessControl.OPERATOR_ROLE); + } + + ValidationLib.validateStakeholder(params.stakeholder_id); + ValidationLib.validateQuantity(params.quantity); // Create and store position - ds.warrantActivePositions.securities[security_id] = WarrantActivePosition({ stakeholder_id: stakeholder_id, quantity: quantity }); + ds.warrantActivePositions.securities[params.security_id] = + WarrantActivePosition({ stakeholder_id: params.stakeholder_id, quantity: params.quantity }); // Track security IDs for this stakeholder - ds.warrantActivePositions.stakeholderToSecurities[stakeholder_id].push(security_id); + ds.warrantActivePositions.stakeholderToSecurities[params.stakeholder_id].push(params.security_id); // Add reverse mapping - ds.warrantActivePositions.securityToStakeholder[security_id] = stakeholder_id; + ds.warrantActivePositions.securityToStakeholder[params.security_id] = params.stakeholder_id; // Store transaction - bytes memory txData = abi.encode(stakeholder_id, quantity, security_id); + bytes memory txData = abi.encode(params); TxHelper.createTx(TxType.WARRANT_ISSUANCE, txData); } + /// @notice Get details of a warrant position + /// @dev Only OPERATOR_ROLE or the stakeholder who owns the position can view it function getWarrantPosition(bytes16 securityId) external view returns (WarrantActivePosition memory) { Storage storage ds = StorageLib.get(); - return ds.warrantActivePositions.securities[securityId]; + + WarrantActivePosition memory position = ds.warrantActivePositions.securities[securityId]; + + // Allow operators and admins to view any position + if (AccessControl.hasOperatorRole(msg.sender) || AccessControl.hasAdminRole(msg.sender)) { + return position; + } + + // Otherwise, verify caller is the stakeholder who owns this position + bytes16 stakeholderId = ds.addressToStakeholderId[msg.sender]; + if (stakeholderId != position.stakeholder_id) { + revert AccessControl.AccessControlUnauthorizedOrInvestor(msg.sender); + } + + return position; } } diff --git a/chain/src/interfaces/IAccessControlFacet.sol b/chain/src/interfaces/IAccessControlFacet.sol new file mode 100644 index 00000000..47d32141 --- /dev/null +++ b/chain/src/interfaces/IAccessControlFacet.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { IAccessControlUpgradeable } from + "openzeppelin-contracts-upgradeable/contracts/access/IAccessControlUpgradeable.sol"; + +interface IAccessControlFacet is IAccessControlUpgradeable { + /// @notice Error definitions from AccessControl + error AccessControlUnauthorized(address account, bytes32 role); + error AccessControlBadConfirmation(); + error AccessControlInvalidTransfer(); + + /// @notice Initialize the access control system + /// @dev Sets up initial roles. The deployer (CapTableFactory) gets admin role + function initializeAccessControl() external; + + /// @notice Initiates transfer of admin role to a new account + /// @dev Only current admin can initiate transfer + /// @param newAdmin Address of the new admin + function transferAdmin(address newAdmin) external; + + /// @notice Accepts admin role transfer + /// @dev Must be called by the pending admin + function acceptAdmin() external; + + /// @notice Returns the current admin address + /// @return The address of the current admin + function getAdmin() external view returns (address); + + /// @notice Returns the pending admin address + /// @return The address of the pending admin + function getPendingAdmin() external view returns (address); + + /// @notice Role definitions + /// @return The OPERATOR_ROLE hash + function OPERATOR_ROLE() external view returns (bytes32); + + /// @notice Role definitions + /// @return The INVESTOR_ROLE hash + function INVESTOR_ROLE() external view returns (bytes32); + + /// @notice Returns whether an account has a role + /// @param role The role to check + /// @param account The account to check + function hasRole(bytes32 role, address account) external view override returns (bool); + + /// @notice Returns the admin role for a role + /// @param role The role to check + function getRoleAdmin(bytes32 role) external view override returns (bytes32); + + /// @notice Grants a role to an account + /// @param role The role to grant + /// @param account The account to grant the role to + function grantRole(bytes32 role, address account) external override; + + /// @notice Revokes a role from an account + /// @param role The role to revoke + /// @param account The account to revoke the role from + function revokeRole(bytes32 role, address account) external override; + + /// @notice Renounces a role for the calling account + /// @param role The role to renounce + /// @param account The account to renounce the role for + function renounceRole(bytes32 role, address account) external override; +} diff --git a/chain/src/interfaces/ICapTable.sol b/chain/src/interfaces/ICapTable.sol deleted file mode 100644 index f6753a24..00000000 --- a/chain/src/interfaces/ICapTable.sol +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import { AccessControlDefaultAdminRules } from "openzeppelin/contracts/access/AccessControlDefaultAdminRules.sol"; -import { Issuer, Stakeholder, StockClass, ActivePositions, SecIdsStockClass, StockLegendTemplate, InitialShares, ShareNumbersIssued, StockParams, StockParamsQuantity, StockIssuanceParams } from "../lib/Structs.sol"; - -interface ICapTable { - // @dev Transactions will be created on-chain then reflected off-chain. - function transactions(uint256 index) external view returns (bytes memory); - - function stakeholderIndex(bytes16 index) external view returns (uint256); - - function stockClassIndex(bytes16 index) external view returns (uint256); - - function walletsPerStakeholder(address wallet) external view returns (bytes16); - - // RBAC - function ADMIN_ROLE() external returns (bytes32); - - function OPERATOR_ROLE() external returns (bytes32); - - /// @notice Initializer for the CapTable, sets access control and initializes issuer struct. - function initialize(bytes16 id, string memory name, uint256 initial_shares_authorized, address admin) external; - - function seedMultipleActivePositionsAndSecurityIds( - bytes16[] calldata stakeholderIds, - bytes16[] calldata securityIds, - bytes16[] calldata stockClassIds, - uint256[] calldata quantities, - uint256[] calldata sharePrices, - uint40[] calldata timestamps - ) external; - - function seedSharesAuthorizedAndIssued(InitialShares calldata params) external; - - function createStakeholder(bytes16 _id, string memory _stakeholder_type, string memory _current_relationship) external; - - function addWalletToStakeholder(bytes16 _stakeholder_id, address _wallet) external; - - function removeWalletFromStakeholder(bytes16 _stakeholder_id, address _wallet) external; - - function getStakeholderIdByWallet(address _wallet) external view returns (bytes16 stakeholderId); - - function acceptStock(bytes16 stakeholderId, bytes16 stockClassId, bytes16 securityId, string[] memory comments) external; - - function adjustIssuerAuthorizedShares( - uint256 newSharesAuthorized, - string[] memory comments, - string memory boardApprovalDate, - string memory stockholderApprovalDate - ) external; - - function adjustStockClassAuthorizedShares( - bytes16 stockClassId, - uint256 newAuthorizedShares, - string[] memory comments, - string memory boardApprovalDate, - string memory stockholderApprovalDate - ) external; - - function createStockClass(bytes16 _id, string memory _class_type, uint256 _price_per_share, uint256 _initial_share_authorized) external; - - function createStockLegendTemplate(bytes16 _id) external; - - function getStakeholderById(bytes16 _id) external view returns (bytes16, string memory, string memory); - - function getStockClassById(bytes16 _id) external view returns (bytes16, string memory, uint256, uint256, uint256); - - function getTotalNumberOfStakeholders() external view returns (uint256); - - function getTotalNumberOfStockClasses() external view returns (uint256); - - function getTransactionsCount() external view returns (uint256); - - function getTotalActiveSecuritiesCount() external view returns (uint256); - - // Function to get the timestamp of an active position - function getActivePosition(bytes16 stakeholderId, bytes16 securityId) external view returns (bytes16, uint, uint, uint40); - - /// @notice Get the avg active position for the stakeholder by dividing the first return value (quantityPrice) by the second (quantity) - /// the timestamp is the time of the latest position - function getAveragePosition(bytes16 stakeholderId, bytes16 stockClassId) external view returns (uint, uint, uint40); - - function issueStock(StockIssuanceParams calldata params) external; - - function repurchaseStock(StockParams calldata params, uint256 quantity, uint256 price) external; - - function retractStockIssuance(StockParams calldata params) external; - - /// Reissuance assumes an issuance transaction has been created and it's tied here under resulting_security_ids - function reissueStock(StockParams calldata params, bytes16[] memory resulting_security_ids) external; - - function cancelStock(StockParams calldata params, uint256 quantity) external; - - function transferStock( - bytes16 transferorStakeholderId, - bytes16 transfereeStakeholderId, - bytes16 stockClassId, - bool isBuyerVerified, - uint256 quantity, - uint256 share_price - ) external; - - function addAdmin(address addr) external; - - function removeAdmin(address addr) external; - - function addOperator(address addr) external; - - function removeOperator(address addr) external; -} diff --git a/chain/src/interfaces/ICapTableFactory.sol b/chain/src/interfaces/ICapTableFactory.sol deleted file mode 100644 index aac578be..00000000 --- a/chain/src/interfaces/ICapTableFactory.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -interface ICapTableFactory { - event CapTableCreated(address indexed capTableProxy); - - function createCapTable(bytes16 id, string memory name, uint256 initial_shares_authorized) external returns (address); - - function updateCapTableImplementation(address newImplementation) external; - - function getCapTableCount() external view returns (uint256); -} diff --git a/chain/src/interfaces/ICapTableInitializer.sol b/chain/src/interfaces/ICapTableInitializer.sol new file mode 100644 index 00000000..24888c0c --- /dev/null +++ b/chain/src/interfaces/ICapTableInitializer.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface ICapTableInitializer { + function initialize(address diamond, bytes16 id, uint256 initialSharesAuthorized, address owner) external; +} diff --git a/chain/src/interfaces/ICaptableFactory.sol b/chain/src/interfaces/ICaptableFactory.sol new file mode 100644 index 00000000..b4d0a199 --- /dev/null +++ b/chain/src/interfaces/ICaptableFactory.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface ICapTableFactory { + /// @notice Emitted when a new cap table is created + /// @param capTable The address of the newly created cap table + /// @param issuerId The ID of the issuer for this cap table + event CapTableCreated(address indexed capTable, bytes16 indexed issuerId); + + /// @notice Creates a new cap table with the specified issuer ID and initial shares + /// @dev Only the owner can create cap tables + /// @param id The unique identifier for the issuer + /// @param initialSharesAuthorized The initial number of authorized shares + /// @return The address of the newly created cap table + function createCapTable(bytes16 id, uint256 initialSharesAuthorized) external returns (address); + + /// @notice Gets the total number of cap tables created by this factory + /// @return The number of cap tables + function getCapTableCount() external view returns (uint256); + + /// @notice Gets the address of a cap table by its index + /// @param index The index of the cap table + /// @return The address of the cap table at the specified index + function capTables(uint256 index) external view returns (address); + + /// @notice Gets the reference diamond address used for copying facets + /// @return The address of the reference diamond + function referenceDiamond() external view returns (address); +} diff --git a/chain/src/interfaces/IConvertiblesFacet.sol b/chain/src/interfaces/IConvertiblesFacet.sol new file mode 100644 index 00000000..393a7ce1 --- /dev/null +++ b/chain/src/interfaces/IConvertiblesFacet.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { ConvertibleActivePosition, IssueConvertibleParams } from "@libraries/Structs.sol"; + +interface IConvertiblesFacet { + /// @notice Issue a new convertible security to a stakeholder + /// @dev Only OPERATOR_ROLE can issue convertibles + /// @param params Parameters for issuing the convertible including stakeholder ID, investment amount, etc. + function issueConvertible(IssueConvertibleParams calldata params) external; + + /// @notice Get details of a convertible position + /// @dev Only OPERATOR_ROLE or the stakeholder who owns the position can view it + /// @param securityId The ID of the convertible security to get details for + /// @return The convertible position details + function getConvertiblePosition(bytes16 securityId) external view returns (ConvertibleActivePosition memory); +} diff --git a/chain/src/interfaces/IEquityCompensationFacet.sol b/chain/src/interfaces/IEquityCompensationFacet.sol new file mode 100644 index 00000000..96bc79b3 --- /dev/null +++ b/chain/src/interfaces/IEquityCompensationFacet.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { EquityCompensationActivePosition, IssueEquityCompensationParams } from "@libraries/Structs.sol"; + +interface IEquityCompensationFacet { + /// @notice Issue equity compensation to a stakeholder + /// @dev Only OPERATOR_ROLE can issue equity compensation + /// @param params Parameters for issuing the equity compensation + function issueEquityCompensation(IssueEquityCompensationParams calldata params) external; + + /// @notice Exercise equity compensation to convert it into stock + /// @dev Only the stakeholder who owns the equity compensation can exercise it + /// @param id The ID of the equity compensation security + /// @param equity_comp_security_id The ID of the equity compensation security to exercise + /// @param resulting_stock_security_id The ID of the stock security that will result from the exercise + /// @param quantity The number of shares to exercise + function exerciseEquityCompensation( + bytes16 id, + bytes16 equity_comp_security_id, + bytes16 resulting_stock_security_id, + uint256 quantity + ) + external; + + /// @notice Get details of an equity compensation position + /// @dev Only OPERATOR_ROLE or the stakeholder who owns the position can view it + /// @param securityId The ID of the equity compensation security to get details for + /// @return The equity compensation position details + function getPosition(bytes16 securityId) external view returns (EquityCompensationActivePosition memory); +} diff --git a/chain/src/interfaces/IIssuerFacet.sol b/chain/src/interfaces/IIssuerFacet.sol new file mode 100644 index 00000000..d926c495 --- /dev/null +++ b/chain/src/interfaces/IIssuerFacet.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Issuer } from "@libraries/Structs.sol"; + +interface IIssuerFacet { + /// @notice Thrown when trying to initialize an already initialized issuer + error IssuerAlreadyInitialized(); + + /// @notice Thrown when invalid shares authorized value is provided + error InvalidSharesAuthorized(); + + /// @notice Emitted when issuer's authorized shares are adjusted + event IssuerAuthorizedSharesAdjusted(uint256 newSharesAuthorized); + + /// @notice Initialize the issuer with initial shares authorized + /// @dev Can only be called once by the factory during setup + /// @param id The unique identifier for the issuer + /// @param initial_shares_authorized Initial number of authorized shares + function initializeIssuer(bytes16 id, uint256 initial_shares_authorized) external; + + /// @notice Adjust the total number of authorized shares for the issuer + /// @dev Only DEFAULT_ADMIN_ROLE can adjust authorized shares + /// @param id The unique identifier for the tx + /// @param newSharesAuthorized New total number of authorized shares + function adjustIssuerAuthorizedShares(bytes16 id, uint256 newSharesAuthorized) external; +} diff --git a/chain/src/interfaces/IStakeholderFacet.sol b/chain/src/interfaces/IStakeholderFacet.sol new file mode 100644 index 00000000..408704e2 --- /dev/null +++ b/chain/src/interfaces/IStakeholderFacet.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { + StockActivePosition, + WarrantActivePosition, + ConvertibleActivePosition, + EquityCompensationActivePosition, + StakeholderPositions +} from "@libraries/Structs.sol"; + +interface IStakeholderFacet { + /// @notice Emitted when a new stakeholder is created + event StakeholderCreated(bytes16 indexed id); + + /// @notice Emitted when a wallet address is linked to a stakeholder + event StakeholderAddressLinked(bytes16 indexed stakeholder_id, address indexed wallet_address); + + /// @notice Thrown when attempting to create a stakeholder that already exists + error StakeholderAlreadyExists(bytes16 stakeholder_id); + + /// @notice Thrown when attempting to link an address that's already linked + error AddressAlreadyLinked(address wallet_address); + + /// @notice Create a new stakeholder + /// @dev Only OPERATOR_ROLE can create stakeholders + /// @param _id The unique identifier for the stakeholder + function createStakeholder(bytes16 _id) external; + + /// @notice Link a wallet address to a stakeholder + /// @dev Only OPERATOR_ROLE can link addresses + /// @param stakeholder_id The stakeholder to link the address to + /// @param wallet_address The address to link + function linkStakeholderAddress(bytes16 stakeholder_id, address wallet_address) external; + + /// @notice Get all positions for a stakeholder + /// @dev INVESTOR_ROLE can only view their own positions, OPERATOR_ROLE and above can view any + /// @param stakeholder_id The stakeholder to get positions for + /// @return All positions (stocks, warrants, convertibles, equity compensation) for the stakeholder + function getStakeholderPositions(bytes16 stakeholder_id) external view returns (StakeholderPositions memory); +} diff --git a/chain/src/interfaces/IStakeholderNFTFacet.sol b/chain/src/interfaces/IStakeholderNFTFacet.sol new file mode 100644 index 00000000..0e213412 --- /dev/null +++ b/chain/src/interfaces/IStakeholderNFTFacet.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { IERC721 } from "openzeppelin-contracts/contracts/token/ERC721/IERC721.sol"; + +interface IStakeholderNFTFacet is IERC721 { + /// @notice Error thrown when caller is not a stakeholder + error NotStakeholder(); + /// @notice Error thrown when NFT is already minted for stakeholder + error AlreadyMinted(); + /// @notice Error thrown when querying URI for non-existent token + error URIQueryForNonexistentToken(); + + /// @notice Mint an NFT representing a stakeholder's position + /// @dev Only stakeholders with INVESTOR_ROLE can mint their own NFT + function mint() external; + + /// @notice Get the URI for a token, containing metadata about stakeholder positions + /// @dev Only OPERATOR_ROLE or the token owner can view the token URI + /// @param tokenId The ID of the token to get URI for + /// @return The token URI containing metadata about stakeholder positions + function tokenURI(uint256 tokenId) external view returns (string memory); +} diff --git a/chain/src/interfaces/IStockClassFacet.sol b/chain/src/interfaces/IStockClassFacet.sol new file mode 100644 index 00000000..8fd6ff04 --- /dev/null +++ b/chain/src/interfaces/IStockClassFacet.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { StockClass } from "@libraries/Structs.sol"; + +interface IStockClassFacet { + /// @notice Emitted when a new stock class is created + event StockClassCreated( + bytes16 indexed id, string indexed classType, uint256 indexed pricePerShare, uint256 initialSharesAuthorized + ); + + /// @notice Emitted when a stock class's authorized shares are adjusted + event StockClassAuthorizedSharesAdjusted(bytes16 indexed stockClassId, uint256 newSharesAuthorized); + + /// @notice Thrown when attempting to create a stock class that already exists + error StockClassAlreadyExists(bytes16 stock_class_id); + + /// @notice Thrown when attempting to operate on a non-existent stock class + error StockClassNotFound(bytes16 stock_class_id); + + /// @notice Thrown when invalid shares authorized value is provided + error InvalidSharesAuthorized(); + + /// @notice Create a new stock class + /// @dev Only DEFAULT_ADMIN_ROLE can create stock classes + /// @param _id Unique identifier for the stock class + /// @param _class_type Type of the stock class (e.g., "Common", "Preferred") + /// @param _price_per_share Price per share in the smallest unit + /// @param _initial_share_authorized Initial number of shares authorized for this class + function createStockClass( + bytes16 _id, + string memory _class_type, + uint256 _price_per_share, + uint256 _initial_share_authorized + ) + external; + + /// @notice Adjust the authorized shares for a stock class + /// @dev Only DEFAULT_ADMIN_ROLE can adjust authorized shares + /// @param id The unique identifier for the tx + /// @param stockClassId ID of the stock class to adjust + /// @param newSharesAuthorized New total number of authorized shares + function adjustAuthorizedShares(bytes16 id, bytes16 stockClassId, uint256 newSharesAuthorized) external; +} diff --git a/chain/src/interfaces/IStockFacet.sol b/chain/src/interfaces/IStockFacet.sol new file mode 100644 index 00000000..419b49b9 --- /dev/null +++ b/chain/src/interfaces/IStockFacet.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { StockActivePosition, IssueStockParams } from "@libraries/Structs.sol"; + +interface IStockFacet { + /// @notice Issue new stock to a stakeholder + /// @dev Only OPERATOR_ROLE can issue stock + /// @param params Parameters for issuing stock including stakeholder ID, stock class ID, quantity, etc. + function issueStock(IssueStockParams calldata params) external; + + /// @notice Get details of a stock position + /// @dev Accessible to INVESTOR_ROLE and above. Investors can only view their own positions + /// @param securityId The ID of the security to get details for + /// @return The stock position details + function getStockPosition(bytes16 securityId) external view returns (StockActivePosition memory); + + /// @notice Get all security IDs for a stakeholder of a specific stock class + /// @dev Accessible to INVESTOR_ROLE and above. Investors can only view their own positions + /// @param stakeholder_id The stakeholder to get securities for + /// @param stock_class_id The stock class to filter by + /// @return Array of security IDs belonging to the stakeholder for the given stock class + function getStakeholderSecurities( + bytes16 stakeholder_id, + bytes16 stock_class_id + ) + external + view + returns (bytes16[] memory); + + /// @notice Transfer stock from one stakeholder to another + /// @dev Only OPERATOR_ROLE can transfer stock + /// @param transferor_stakeholder_id The stakeholder transferring the stock + /// @param transferee_stakeholder_id The stakeholder receiving the stock + /// @param stock_class_id The stock class being transferred + /// @param quantity The number of shares to transfer + /// @param share_price The price per share for the transfer + function transferStock( + bytes16 transferor_stakeholder_id, + bytes16 transferee_stakeholder_id, + bytes16 stock_class_id, + uint256 quantity, + uint256 share_price + ) + external; +} diff --git a/chain/src/interfaces/IStockPlanFacet.sol b/chain/src/interfaces/IStockPlanFacet.sol new file mode 100644 index 00000000..4e9a94ab --- /dev/null +++ b/chain/src/interfaces/IStockPlanFacet.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { StockPlan } from "@libraries/Structs.sol"; + +interface IStockPlanFacet { + /// @notice Emitted when a new stock plan is created + event StockPlanCreated(bytes16 indexed id, uint256 shares_reserved); + + /// @notice Emitted when a stock plan's reserved shares are adjusted + event StockPlanSharesReservedAdjusted(bytes16 indexed id, uint256 newSharesReserved); + + /// @notice Thrown when attempting to create a stock plan that already exists + error StockPlanAlreadyExists(bytes16 stock_plan_id); + + /// @notice Thrown when referencing an invalid stock class + error InvalidStockClass(bytes16 stock_class_id); + + /// @notice Thrown when attempting to operate on a non-existent stock plan + error StockPlanNotFound(bytes16 stock_plan_id); + + /// @notice Create a new stock plan with specified stock classes and reserved shares + /// @dev Only OPERATOR_ROLE can create stock plans + /// @param id Unique identifier for the stock plan + /// @param stock_class_ids Array of stock class IDs that can be issued under this plan + /// @param shares_reserved Number of shares reserved for this plan + function createStockPlan(bytes16 id, bytes16[] memory stock_class_ids, uint256 shares_reserved) external; + + /// @notice Adjust the number of shares reserved in a stock plan + /// @dev Only OPERATOR_ROLE can adjust stock plan pools + /// @param id Unique identifier for the stock plan + /// @param stockPlanId ID of the stock plan to adjust + /// @param newSharesReserved New number of shares reserved for the plan + function adjustStockPlanPool(bytes16 id, bytes16 stockPlanId, uint256 newSharesReserved) external; +} diff --git a/chain/src/interfaces/IWarrantFacet.sol b/chain/src/interfaces/IWarrantFacet.sol new file mode 100644 index 00000000..718d3362 --- /dev/null +++ b/chain/src/interfaces/IWarrantFacet.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { WarrantActivePosition, IssueWarrantParams } from "@libraries/Structs.sol"; + +interface IWarrantFacet { + /// @notice Issue a new warrant to a stakeholder + /// @dev Only OPERATOR_ROLE can issue warrants + /// @param params Parameters for issuing the warrant including stakeholder ID, quantity, etc. + function issueWarrant(IssueWarrantParams calldata params) external; + + /// @notice Get details of a warrant position + /// @dev Only OPERATOR_ROLE or the stakeholder who owns the position can view it + /// @param securityId The ID of the warrant security to get details for + /// @return The warrant position details + function getWarrantPosition(bytes16 securityId) external view returns (WarrantActivePosition memory); +} diff --git a/chain/src/lib/DeleteContext.sol b/chain/src/lib/DeleteContext.sol deleted file mode 100644 index b85ecebe..00000000 --- a/chain/src/lib/DeleteContext.sol +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import { ActivePositions, SecIdsStockClass } from "./Structs.sol"; - -library DeleteContext { - function deleteActivePosition(bytes16 _stakeholder_id, bytes16 _security_id, ActivePositions storage positions) external { - delete positions.activePositions[_stakeholder_id][_security_id]; - } - - // Active Security IDs by Stock Class { "stakeholder_id": { "stock_class_id-1": ["sec-id-1", "sec-id-2"] } } - function deleteActiveSecurityIdsByStockClass( - bytes16 _stakeholder_id, - bytes16 _stock_class_id, - bytes16 _security_id, - SecIdsStockClass storage activeSecs - ) external { - bytes16[] storage securities = activeSecs.activeSecurityIdsByStockClass[_stakeholder_id][_stock_class_id]; - - uint256 index = find(securities, _security_id); - if (index != type(uint256).max) { - remove(securities, index); - } - } - - /** - * @dev Searches for an element in a bytes16 array and returns its index. - * Returns uint256(-1) if the element is not found. - * @param array The array to search in. - * @param element The element to search for. - */ - function find(bytes16[] storage array, bytes16 element) internal view returns (uint256) { - for (uint256 i = 0; i < array.length; i++) { - if (array[i] == element) { - return i; - } - } - return type(uint256).max; // Return the maximum value of uint256 to indicate "not found" - } - - /** - * @dev Removes an element at a specific index in a bytes16 array. - * Shifts all subsequent elements one position to the left and reduces the array length. - * @param array The array to modify. - * @param index The index of the element to remove. - */ - function remove(bytes16[] storage array, uint256 index) internal { - require(index < array.length, "Index out of bounds"); - - for (uint256 i = index; i < array.length - 1; i++) { - array[i] = array[i + 1]; - } - array.pop(); - } -} diff --git a/chain/src/lib/Stock.sol b/chain/src/lib/Stock.sol deleted file mode 100644 index 9dde6193..00000000 --- a/chain/src/lib/Stock.sol +++ /dev/null @@ -1,359 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import { StockIssuance, ActivePosition, ShareNumbersIssued, ActivePositions, SecIdsStockClass, Issuer, StockClass, StockIssuanceParams, StockParams } from "./Structs.sol"; -import "./TxHelper.sol"; -import "./DeleteContext.sol"; - -library StockLib { - error InsufficientShares(uint256 available, uint256 required); - error InvalidQuantityOrPrice(uint256 quantity, uint256 price); - error UnverifiedBuyer(); - error ActivePositionNotFound(bytes16 stakeholderId, bytes16 securityId); - - function createIssuance( - uint256 nonce, - StockIssuanceParams memory issuanceParams, - ActivePositions storage positions, - SecIdsStockClass storage activeSecs, - bytes[] storage transactions, - Issuer storage issuer, - StockClass storage stockClass - ) external { - _checkInvalidQuantityOrPrice(issuanceParams.quantity, issuanceParams.share_price); - - StockIssuance memory issuance = TxHelper.createStockIssuanceStruct(issuanceParams, nonce); - _updateContext(issuance, positions, activeSecs, issuer, stockClass, transactions); - } - - function createTransfer( - StockTransferParams memory params, - ActivePositions storage positions, - SecIdsStockClass storage activeSecs, - bytes[] storage transactions, - Issuer storage issuer, - StockClass storage stockClass - ) external { - _checkBuyerVerified(params.is_buyer_verified); - _checkInvalidQuantityOrPrice(params.quantity, params.share_price); - require( - activeSecs.activeSecurityIdsByStockClass[params.transferor_stakeholder_id][params.stock_class_id].length > 0, - "No active security ids found" - ); - bytes16[] memory activeSecurityIDs = activeSecs.activeSecurityIdsByStockClass[params.transferor_stakeholder_id][params.stock_class_id]; - - uint256 sum = 0; - uint256 numSecurityIds = 0; - - for (uint256 index = 0; index < activeSecurityIDs.length; index++) { - ActivePosition storage activePosition = positions.activePositions[params.transferor_stakeholder_id][activeSecurityIDs[index]]; - sum += activePosition.quantity; - - numSecurityIds += 1; - if (sum >= params.quantity) { - break; - } - } - - _checkInsuffientAmount(sum, params.quantity); - - uint256 remainingQuantity = params.quantity; - - for (uint256 index = 0; index < numSecurityIds; index++) { - bytes16 active_security_id = activeSecurityIDs[index]; - - ActivePosition storage activePosition = positions.activePositions[params.transferor_stakeholder_id][active_security_id]; - - uint256 transferQuantity = remainingQuantity; - - if (activePosition.quantity <= remainingQuantity) { - transferQuantity = activePosition.quantity; - } - - params.quantity = transferQuantity; - - _transferSingleStock(params, active_security_id, positions, activeSecs, transactions, issuer, stockClass); - - remainingQuantity -= transferQuantity; - - if (remainingQuantity == 0) { - break; - } - } - } - - function createCancellation( - StockParamsQuantity memory params, - ActivePositions storage positions, - SecIdsStockClass storage activeSecs, - bytes[] storage transactions, - Issuer storage issuer, - StockClass storage stockClass - ) external { - ActivePosition memory activePosition = positions.activePositions[params.stakeholder_id][params.security_id]; - - _checkActivePositionExists(activePosition, params.stakeholder_id, params.security_id); - _checkInsuffientAmount(activePosition.quantity, params.quantity); - - uint256 remainingQuantity = activePosition.quantity - params.quantity; - bytes16 balance_security_id = ""; - - if (remainingQuantity > 0) { - StockTransferParams memory transferParams = StockTransferParams( - params.stakeholder_id, - bytes16(0), - params.stock_class_id, - true, - remainingQuantity, - activePosition.share_price, - params.nonce - ); - StockIssuance memory balanceIssuance = TxHelper.createStockIssuanceStructForTransfer( - transferParams, - transferParams.transferor_stakeholder_id - ); - - _updateContext(balanceIssuance, positions, activeSecs, issuer, stockClass, transactions); - - balance_security_id = balanceIssuance.security_id; - } - - StockCancellation memory cancellation = TxHelper.createStockCancellationStruct( - params.nonce, - params.quantity, - params.comments, - params.security_id, - params.reason_text, - balance_security_id - ); - - TxHelper.createTx(TxType.STOCK_CANCELLATION, abi.encode(cancellation), transactions); - - _subtractSharesIssued(issuer, stockClass, activePosition.quantity); - - DeleteContext.deleteActivePosition(params.stakeholder_id, params.security_id, positions); - DeleteContext.deleteActiveSecurityIdsByStockClass(params.stakeholder_id, params.stock_class_id, params.security_id, activeSecs); - } - - function createReissuance( - StockParams memory params, - uint256 nonce, - bytes16[] memory resulting_security_ids, - ActivePositions storage positions, - SecIdsStockClass storage activeSecs, - bytes[] storage transactions, - Issuer storage issuer, - StockClass storage stockClass - ) external { - ActivePosition memory activePosition = positions.activePositions[params.stakeholder_id][params.security_id]; - - _checkActivePositionExists(activePosition, params.stakeholder_id, params.security_id); - - StockReissuance memory reissuance = TxHelper.createStockReissuanceStruct( - nonce, - params.comments, - params.security_id, - resulting_security_ids, - params.reason_text - ); - - TxHelper.createTx(TxType.STOCK_REISSUANCE, abi.encode(reissuance), transactions); - - _subtractSharesIssued(issuer, stockClass, activePosition.quantity); - - DeleteContext.deleteActivePosition(params.stakeholder_id, params.security_id, positions); - DeleteContext.deleteActiveSecurityIdsByStockClass(params.stakeholder_id, params.stock_class_id, params.security_id, activeSecs); - } - - function createRepurchase( - StockParamsQuantity memory params, - uint256 price, - ActivePositions storage positions, - SecIdsStockClass storage activeSecs, - bytes[] storage transactions, - Issuer storage issuer, - StockClass storage stockClass - ) external { - ActivePosition memory activePosition = positions.activePositions[params.stakeholder_id][params.security_id]; - - _checkActivePositionExists(activePosition, params.stakeholder_id, params.security_id); - _checkInsuffientAmount(activePosition.quantity, params.quantity); - - uint256 remainingQuantity = activePosition.quantity - params.quantity; - bytes16 balance_security_id = ""; - - if (remainingQuantity > 0) { - StockTransferParams memory transferParams = StockTransferParams( - params.stakeholder_id, - bytes16(0), - params.stock_class_id, - true, - remainingQuantity, - activePosition.share_price, - params.nonce - ); - StockIssuance memory balanceIssuance = TxHelper.createStockIssuanceStructForTransfer( - transferParams, - transferParams.transferor_stakeholder_id - ); - - _updateContext(balanceIssuance, positions, activeSecs, issuer, stockClass, transactions); - - balance_security_id = balanceIssuance.security_id; - } - - StockRepurchase memory repurchase = TxHelper.createStockRepurchaseStruct(params, price); - - TxHelper.createTx(TxType.STOCK_REPURCHASE, abi.encode(repurchase), transactions); - - _subtractSharesIssued(issuer, stockClass, activePosition.quantity); - - DeleteContext.deleteActivePosition(params.stakeholder_id, params.security_id, positions); - DeleteContext.deleteActiveSecurityIdsByStockClass(params.stakeholder_id, params.stock_class_id, params.security_id, activeSecs); - } - - function createRetraction( - StockParams memory params, - uint256 nonce, - ActivePositions storage positions, - SecIdsStockClass storage activeSecs, - bytes[] storage transactions, - Issuer storage issuer, - StockClass storage stockClass - ) external { - ActivePosition memory activePosition = positions.activePositions[params.stakeholder_id][params.security_id]; - - _checkActivePositionExists(activePosition, params.stakeholder_id, params.security_id); - - StockRetraction memory retraction = TxHelper.createStockRetractionStruct(nonce, params.comments, params.security_id, params.reason_text); - TxHelper.createTx(TxType.STOCK_RETRACTION, abi.encode(retraction), transactions); - - _subtractSharesIssued(issuer, stockClass, activePosition.quantity); - - DeleteContext.deleteActivePosition(params.stakeholder_id, params.security_id, positions); - DeleteContext.deleteActiveSecurityIdsByStockClass(params.stakeholder_id, params.stock_class_id, params.security_id, activeSecs); - } - - function createAcceptance(uint256 nonce, bytes16 securityId, string[] memory comments, bytes[] storage transactions) external { - StockAcceptance memory acceptance = TxHelper.createStockAcceptanceStruct(nonce, comments, securityId); - - TxHelper.createTx(TxType.STOCK_ACCEPTANCE, abi.encode(acceptance), transactions); - } - - function _updateContext( - StockIssuance memory issuance, - ActivePositions storage positions, - SecIdsStockClass storage activeSecs, - Issuer storage issuer, - StockClass storage stockClass, - bytes[] storage transactions - ) internal { - activeSecs.activeSecurityIdsByStockClass[issuance.params.stakeholder_id][issuance.params.stock_class_id].push(issuance.security_id); - - positions.activePositions[issuance.params.stakeholder_id][issuance.security_id] = ActivePosition( - issuance.params.stock_class_id, - issuance.params.quantity, - issuance.params.share_price, - _safeNow() // TODO: only using current datetime doesn't allow us to support backfilling transactions. - ); - - issuer.shares_issued = issuer.shares_issued + issuance.params.quantity; - stockClass.shares_issued = stockClass.shares_issued + issuance.params.quantity; - - TxHelper.createTx(TxType.STOCK_ISSUANCE, abi.encode(issuance), transactions); - } - - function _safeNow() internal view returns (uint40) { - return uint40(block.timestamp); - } - - function _subtractSharesIssued(Issuer storage issuer, StockClass storage stockClass, uint256 quantity) internal { - issuer.shares_issued = issuer.shares_issued - quantity; - stockClass.shares_issued = stockClass.shares_issued - quantity; - } - - // isBuyerVerified is a placeholder for a signature, account or hash that confirms the buyer's identity. TODO: delete if not necessary - function _transferSingleStock( - StockTransferParams memory params, - bytes16 transferorSecurityId, - ActivePositions storage positions, - SecIdsStockClass storage activeSecs, - bytes[] storage transactions, - Issuer storage issuer, - StockClass storage stockClass - ) internal { - ActivePosition memory transferorActivePosition = positions.activePositions[params.transferor_stakeholder_id][transferorSecurityId]; - - _checkInsuffientAmount(transferorActivePosition.quantity, params.quantity); - - StockIssuance memory transfereeIssuance = TxHelper.createStockIssuanceStructForTransfer(params, params.transferee_stakeholder_id); - - _updateContext(transfereeIssuance, positions, activeSecs, issuer, stockClass, transactions); - - uint256 balanceForTransferor = transferorActivePosition.quantity - params.quantity; - - bytes16 balance_security_id = ""; - - StockTransferParams memory newParams = StockTransferParams( - params.transferor_stakeholder_id, - params.transferee_stakeholder_id, - params.stock_class_id, - params.is_buyer_verified, - params.quantity, - params.share_price, - params.nonce - ); - newParams.quantity = balanceForTransferor; - newParams.share_price = transferorActivePosition.share_price; - - if (balanceForTransferor > 0) { - StockIssuance memory transferorBalanceIssuance = TxHelper.createStockIssuanceStructForTransfer( - newParams, - newParams.transferor_stakeholder_id - ); - - _updateContext(transferorBalanceIssuance, positions, activeSecs, issuer, stockClass, transactions); - - balance_security_id = transferorBalanceIssuance.security_id; - } - - StockTransfer memory transfer = TxHelper.createStockTransferStruct( - params.nonce, - params.quantity, - transferorSecurityId, - transfereeIssuance.security_id, - balance_security_id - ); - - TxHelper.createTx(TxType.STOCK_TRANSFER, abi.encode(transfer), transactions); - - _subtractSharesIssued(issuer, stockClass, transferorActivePosition.quantity); - - DeleteContext.deleteActivePosition(params.transferor_stakeholder_id, transferorSecurityId, positions); - DeleteContext.deleteActiveSecurityIdsByStockClass(params.transferor_stakeholder_id, params.stock_class_id, transferorSecurityId, activeSecs); - } - - function _checkInvalidQuantityOrPrice(uint256 quantity, uint256 price) internal pure { - if (quantity <= 0 || price <= 0) { - revert InvalidQuantityOrPrice(quantity, price); - } - } - - function _checkInsuffientAmount(uint256 available, uint256 desired) internal pure { - if (available < desired) { - revert InsufficientShares(available, desired); - } - } - - function _checkActivePositionExists(ActivePosition memory activePosition, bytes16 stakeholderId, bytes16 securityId) internal pure { - if (activePosition.quantity == 0) { - revert ActivePositionNotFound(stakeholderId, securityId); - } - } - - function _checkBuyerVerified(bool isBuyerVerified) internal pure { - if (!isBuyerVerified) { - revert UnverifiedBuyer(); - } - } -} diff --git a/chain/src/lib/Structs.sol b/chain/src/lib/Structs.sol deleted file mode 100644 index cd8bc92c..00000000 --- a/chain/src/lib/Structs.sol +++ /dev/null @@ -1,198 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -// My bet is that Issuer and Stock Class need to have a quanity field where the shares are added back for retractions or cancellations -// On issuance, we must verify there are enough shares available to issue for that stock class - -struct Issuer { - bytes16 id; - string legal_name; - uint256 shares_issued; - uint256 shares_authorized; -} - -// can be later extended to add things like seniority, conversion_rights, etc. -struct StockClass { - bytes16 id; - string class_type; // ["COMMON", "PREFERRED"] - uint256 price_per_share; // Per-share price this stock class was issued for - uint256 shares_issued; - uint256 shares_authorized; -} - -struct Stakeholder { - bytes16 id; - string stakeholder_type; // ["INDIVIDUAL", "INSTITUTION"] - string current_relationship; // ["ADVISOR","BOARD_MEMBER","CONSULTANT","EMPLOYEE","EX_ADVISOR" "EX_CONSULTANT","EX_EMPLOYEE","EXECUTIVE","FOUNDER","INVESTOR","NON_US_EMPLOYEE","OFFICER","OTHER"] -} - -struct ActivePosition { - bytes16 stock_class_id; - uint256 quantity; - uint256 share_price; - uint40 timestamp; -} - -struct ShareNumbersIssued { - uint256 starting_share_number; - uint256 ending_share_number; -} - -struct IssuerInitialShares { - uint256 shares_authorized; - uint256 shares_issued; -} - -struct StockClassInitialShares { - bytes16 id; - uint256 shares_authorized; - uint256 shares_issued; -} - -struct InitialShares { - IssuerInitialShares issuerInitialShares; - StockClassInitialShares[] stockClassesInitialShares; -} - -struct StockCancellation { - bytes16 id; - string object_type; - uint256 quantity; - string[] comments; // optional - bytes16 security_id; - string reason_text; // optional - bytes16 balance_security_id; // optional -} - -struct StockRetraction { - bytes16 id; - string object_type; - string[] comments; // optional - bytes16 security_id; - string reason_text; // optional -} - -struct StockReissuance { - bytes16 id; - string object_type; - string[] comments; // optional - bytes16 security_id; - bytes16[] resulting_security_ids; - bytes16 split_transaction_id; // not used in MVP - string reason_text; -} - -struct StockRepurchase { - bytes16 id; - string object_type; - string[] comments; // optional - bytes16 security_id; - string consideration_text; // optional, - bytes16 balance_security_id; // optional - uint256 quantity; - uint256 price; -} - -struct StockAcceptance { - bytes16 id; - string object_type; - bytes16 security_id; - string[] comments; // optional -} - -struct IssuerAuthorizedSharesAdjustment { - bytes16 id; - string object_type; - uint256 new_shares_authorized; - string[] comments; // optional - string board_approval_date; // optional - string stockholder_approval_date; // optional -} - -struct StockClassAuthorizedSharesAdjustment { - bytes16 id; - string object_type; - uint256 new_shares_authorized; - string[] comments; // optional - string board_approval_date; // optional - string stockholder_approval_date; // optional -} - -// date fields are going to use block timestamp -struct StockIssuance { - bytes16 id; - string object_type; - bytes16 security_id; - StockIssuanceParams params; -} - -struct StockLegendTemplate { - bytes16 id; -} - -struct StockParamsQuantity { - uint256 nonce; - uint256 quantity; - bytes16 stakeholder_id; - bytes16 stock_class_id; - bytes16 security_id; - string[] comments; - string reason_text; -} - -struct StockParams { - bytes16 stakeholder_id; // not OCF, but required to fetch activePositions - bytes16 stock_class_id; // not OCF, but required to fetch activePositions - bytes16 security_id; - string[] comments; - string reason_text; -} - -struct StockTransferParams { - bytes16 transferor_stakeholder_id; - bytes16 transferee_stakeholder_id; - bytes16 stock_class_id; - bool is_buyer_verified; - uint256 quantity; - uint256 share_price; - uint256 nonce; -} - -struct StockIssuanceParams { - bytes16 stock_class_id; - bytes16 stock_plan_id; // Optional - ShareNumbersIssued share_numbers_issued; // Optional - uint256 share_price; // OCF Monetary (USD is all that matters). Amount is Numeric: Fixed-point string representation of a number (up to 10 decimal places supported) - uint256 quantity; // Numeric: Fixed-point string representation of a number (up to 10 decimal places supported) - bytes16 vesting_terms_id; // Optional - uint256 cost_basis; // Optional OCF Monetary (USD is all that matters). Amount is Numeric: Fixed-point string representation of a number (up to 10 decimal places supported) - bytes16[] stock_legend_ids; // Optional - string issuance_type; // Optional for special types (["RSA", "FOUNDERS_STOCK"],) - string[] comments; // Optional - string custom_id; // Optional (eg R2-D2) - bytes16 stakeholder_id; - string board_approval_date; // Optional - string stockholder_approval_date; // Optional - string consideration_text; // Optional - string[] security_law_exemptions; // Unclear -} - -// date fields are going to use block timestamp -struct StockTransfer { - bytes16 id; - string object_type; - uint256 quantity; - string[] comments; // optional - bytes16 security_id; - string consideration_text; // optional - bytes16 balance_security_id; // optional - bytes16[] resulting_security_ids; -} - -struct ActivePositions { - mapping(bytes16 => mapping(bytes16 => ActivePosition)) activePositions; -} - -struct SecIdsStockClass { - mapping(bytes16 => mapping(bytes16 => bytes16[])) activeSecurityIdsByStockClass; -} diff --git a/chain/src/lib/TxHelper.sol b/chain/src/lib/TxHelper.sol deleted file mode 100644 index 502e88a4..00000000 --- a/chain/src/lib/TxHelper.sol +++ /dev/null @@ -1,222 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import { StockIssuance, StockIssuanceParams, ActivePositions, ActivePosition, SecIdsStockClass, Issuer, StockClass, StockTransfer, StockRepurchase, ShareNumbersIssued, StockAcceptance, StockCancellation, StockReissuance, StockRetraction, IssuerAuthorizedSharesAdjustment, StockClassAuthorizedSharesAdjustment, StockTransferParams, StockParamsQuantity, StockIssuanceParams } from "./Structs.sol"; - -enum TxType { - INVALID, - ISSUER_AUTHORIZED_SHARES_ADJUSTMENT, - STOCK_CLASS_AUTHORIZED_SHARES_ADJUSTMENT, - STOCK_ACCEPTANCE, - STOCK_CANCELLATION, - STOCK_ISSUANCE, - STOCK_REISSUANCE, - STOCK_REPURCHASE, - STOCK_RETRACTION, - STOCK_TRANSFER -} - -struct Tx { - TxType txType; - bytes txData; -} - -library TxHelper { - event TxCreated(uint256 index, TxType txType, bytes txData); - - function createTx(TxType txType, bytes memory txData, bytes[] storage transactions) internal { - transactions.push(txData); - emit TxCreated(transactions.length, txType, txData); - } - - function generateDeterministicUniqueID(bytes16 stakeholderId, uint256 nonce) public view returns (bytes16) { - bytes16 deterministicValue = bytes16(keccak256(abi.encodePacked(stakeholderId, block.timestamp, block.prevrandao, nonce))); - return deterministicValue; - } - - function createStockIssuanceStructByTA(uint256 nonce, StockIssuanceParams memory params) internal view returns (StockIssuance memory issuance) { - bytes16 id = generateDeterministicUniqueID(params.stakeholder_id, nonce); - bytes16 secId = generateDeterministicUniqueID(params.stock_class_id, nonce); - - return StockIssuance(id, "TX_STOCK_ISSUANCE", secId, params); - } - - // TODO: do we need to have more information from the previous transferor issuance in this new issuance? - // I think we can extend this for all other types of balances - function createStockIssuanceStructForTransfer( - StockTransferParams memory transferParams, - bytes16 stakeholderId - ) internal view returns (StockIssuance memory issuance) { - ShareNumbersIssued memory share_numbers_issued; // if not instatiated it defaults to 0 for both values - - bytes16 id = generateDeterministicUniqueID(stakeholderId, transferParams.nonce); - bytes16 securityId = generateDeterministicUniqueID(transferParams.stock_class_id, transferParams.nonce); - - StockIssuanceParams memory params = StockIssuanceParams( - transferParams.stock_class_id, // Stock class ID - "", // Stock plan ID (optional) - share_numbers_issued, // Share numbers issued (optional) - transferParams.share_price, // Share price - transferParams.quantity, // Quantity - "", // Vesting terms ID (optional) - 0e10, // Cost basis (optional) - new bytes16[](0), // Stock legend IDs (optional) - "", // Issuance type (optional) - new string[](0), // Comments - "", // Custom ID (optional) - stakeholderId, // Stakeholder ID - "", // Board approval date (optional) - "", // Stockholder approval date (optional) - "", // Consideration text (optional) - new string[](0) // Security law exemptions (optional) - ); - return - StockIssuance( - id, // ID - "TX_STOCK_ISSUANCE", // Transaction type - securityId, // Security ID - params - ); - } - - function createStockIssuanceStruct( - StockIssuanceParams memory issuanceParams, - uint256 nonce - ) internal view returns (StockIssuance memory issuance) { - bytes16 id = generateDeterministicUniqueID(issuanceParams.stakeholder_id, nonce); - bytes16 secId = generateDeterministicUniqueID(issuanceParams.stock_class_id, nonce); - - return StockIssuance(id, "TX_STOCK_ISSUANCE", secId, issuanceParams); - } - - function createStockTransferStruct( - uint256 nonce, - uint256 quantity, - bytes16 security_id, - bytes16 resulting_security_id, - bytes16 balance_security_id - ) internal view returns (StockTransfer memory transfer) { - bytes16[] memory resultingSecurityIds = new bytes16[](1); - resultingSecurityIds[0] = resulting_security_id; - - bytes16 id = generateDeterministicUniqueID(security_id, nonce); - - return - StockTransfer( - id, - "TX_STOCK_TRANSFER", - quantity, - new string[](0), // comments, - security_id, - "", // consideration text (optional) //TODO: should we include in cap table? - balance_security_id, - resultingSecurityIds - ); - } - - function createStockCancellationStruct( - uint256 nonce, - uint256 quantity, - string[] memory comments, - bytes16 securityId, - string memory reasonText, - bytes16 balance_security_id - ) internal view returns (StockCancellation memory cancellation) { - bytes16 id = generateDeterministicUniqueID(securityId, nonce); - - return StockCancellation(id, "TX_STOCK_CANCELLATION", quantity, comments, securityId, reasonText, balance_security_id); - } - - function createStockRetractionStruct( - uint256 nonce, - string[] memory comments, - bytes16 securityId, - string memory reasonText - ) internal view returns (StockRetraction memory retraction) { - bytes16 id = generateDeterministicUniqueID(securityId, nonce); - - return StockRetraction(id, "TX_STOCK_RETRACTION", comments, securityId, reasonText); - } - - function createStockRepurchaseStruct(StockParamsQuantity memory params, uint256 price) internal view returns (StockRepurchase memory repurchase) { - bytes16 id = generateDeterministicUniqueID(params.security_id, params.nonce); - - // Note: using stakeholderId to store balanceSecurityId - return - StockRepurchase( - id, - "TX_STOCK_REPURCHASE", - params.comments, - params.security_id, - params.reason_text, - params.stakeholder_id, - params.quantity, - price - ); - } - - function adjustIssuerAuthorizedShares( - uint256 nonce, - uint256 newSharesAuthorized, - string[] memory comments, - string memory boardApprovalDate, - string memory stockholderApprovalDate, - bytes16 issuerId - ) internal view returns (IssuerAuthorizedSharesAdjustment memory adjustment) { - bytes16 id = generateDeterministicUniqueID(issuerId, nonce); - - return - IssuerAuthorizedSharesAdjustment( - id, - "TX_ISSUER_AUTHORIZED_SHARES_ADJUSTMENT", - newSharesAuthorized, - comments, - boardApprovalDate, - stockholderApprovalDate - ); - } - - function adjustStockClassAuthorizedShares( - uint256 nonce, - uint256 newSharesAuthorized, - string[] memory comments, - string memory boardApprovalDate, - string memory stockholderApprovalDate, - bytes16 stockClassId - ) internal view returns (StockClassAuthorizedSharesAdjustment memory adjustment) { - bytes16 id = generateDeterministicUniqueID(stockClassId, nonce); - - return - StockClassAuthorizedSharesAdjustment( - id, - "TX_STOCK_CLASS_AUTHORIZED_SHARES_ADJUSTMENT", - newSharesAuthorized, - comments, - boardApprovalDate, - stockholderApprovalDate - ); - } - - function createStockReissuanceStruct( - uint256 nonce, - string[] memory comments, - bytes16 securityId, - bytes16[] memory resultingSecurityIds, - string memory reasonText - ) internal view returns (StockReissuance memory reissuance) { - bytes16 id = generateDeterministicUniqueID(securityId, nonce); - bytes16 splitTransactionId = bytes16(0); // Not used in MVP - - return StockReissuance(id, "TX_STOCK_REISSUANCE", comments, securityId, resultingSecurityIds, splitTransactionId, reasonText); - } - - function createStockAcceptanceStruct( - uint256 nonce, - string[] memory comments, - bytes16 securityId - ) internal view returns (StockAcceptance memory acceptance) { - bytes16 id = generateDeterministicUniqueID(securityId, nonce); - - return StockAcceptance(id, "TX_STOCK_ACCEPTANCE", securityId, comments); - } -} diff --git a/chain/src/lib/transactions/Adjustment.sol b/chain/src/lib/transactions/Adjustment.sol deleted file mode 100644 index 3cf6d134..00000000 --- a/chain/src/lib/transactions/Adjustment.sol +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import { Issuer, StockClass } from "../Structs.sol"; -import "../TxHelper.sol"; - -library Adjustment { - function adjustIssuerAuthorizedShares( - uint256 nonce, - uint256 newSharesAuthorized, - string[] memory comments, - string memory boardApprovalDate, - string memory stockholderApprovalDate, - Issuer storage issuer, - bytes[] storage transactions - ) external { - nonce++; - IssuerAuthorizedSharesAdjustment memory adjustment = TxHelper.adjustIssuerAuthorizedShares( - nonce, - newSharesAuthorized, - comments, - boardApprovalDate, - stockholderApprovalDate, - issuer.id - ); - - issuer.shares_authorized = newSharesAuthorized; - - TxHelper.createTx(TxType.ISSUER_AUTHORIZED_SHARES_ADJUSTMENT, abi.encode(adjustment), transactions); - } - - function adjustStockClassAuthorizedShares( - uint256 nonce, - uint256 newSharesAuthorized, - string[] memory comments, - string memory boardApprovalDate, - string memory stockholderApprovalDate, - StockClass storage stockClass, - bytes[] storage transactions - ) external { - nonce++; - StockClassAuthorizedSharesAdjustment memory adjustment = TxHelper.adjustStockClassAuthorizedShares( - nonce, - newSharesAuthorized, - comments, - boardApprovalDate, - stockholderApprovalDate, - stockClass.id - ); - - stockClass.shares_authorized = newSharesAuthorized; - - TxHelper.createTx(TxType.STOCK_CLASS_AUTHORIZED_SHARES_ADJUSTMENT, abi.encode(adjustment), transactions); - } -} diff --git a/chain/src/libraries/AccessControl.sol b/chain/src/libraries/AccessControl.sol new file mode 100644 index 00000000..5a368024 --- /dev/null +++ b/chain/src/libraries/AccessControl.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Storage, StorageLib } from "@core/Storage.sol"; + +library AccessControl { + // Role definitions - matching AccessControlFacet + bytes32 internal constant DEFAULT_ADMIN_ROLE = 0x00; + bytes32 internal constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); + bytes32 internal constant INVESTOR_ROLE = keccak256("INVESTOR_ROLE"); + + error AccessControlUnauthorized(address account, bytes32 role); + error AccessControlUnauthorizedOrInvestor(address account); + + /// @dev Helper to check if an account has admin role + function hasAdminRole(address account) internal view returns (bool) { + return StorageLib.get().roles[DEFAULT_ADMIN_ROLE][account]; + } + + /// @dev Helper to check if an account has operator role + function hasOperatorRole(address account) internal view returns (bool) { + return StorageLib.get().roles[OPERATOR_ROLE][account]; + } + + /// @dev Helper to check if an account has investor role + function hasInvestorRole(address account) internal view returns (bool) { + return StorageLib.get().roles[INVESTOR_ROLE][account]; + } +} diff --git a/chain/src/libraries/Structs.sol b/chain/src/libraries/Structs.sol index dc89a97b..c22c4cf1 100644 --- a/chain/src/libraries/Structs.sol +++ b/chain/src/libraries/Structs.sol @@ -11,8 +11,8 @@ struct Issuer { struct StockClass { bytes16 id; string class_type; // ["COMMON", "PREFERRED"] - uint256 price_per_share; // Per-share price this stock class was issued for // remove this. uint256 shares_issued; + uint256 price_per_share; uint256 shares_authorized; } @@ -37,9 +37,6 @@ struct StockActivePositions { struct ConvertibleActivePosition { bytes16 stakeholder_id; uint256 investment_amount; - // uint256 valuation_cap; // unsure we want to store this - // uint256 discount_rate; // unsure we want to store this - // string convertible_type; // ["NOTE", "SAFE"] // do we even care? } struct ConvertibleActivePositions { @@ -85,3 +82,67 @@ struct StakeholderPositions { ConvertibleActivePosition[] convertibles; EquityCompensationActivePosition[] equityCompensations; } + +struct IssueStockParams { + bytes16 id; + bytes16 stock_class_id; + uint256 share_price; + uint256 quantity; + bytes16 stakeholder_id; + bytes16 security_id; + string custom_id; + string stock_legend_ids_mapping; + string security_law_exemptions_mapping; +} + +struct IssueConvertibleParams { + bytes16 id; + bytes16 stakeholder_id; + uint256 investment_amount; + bytes16 security_id; + string convertible_type; + uint256 seniority; + string custom_id; + string security_law_exemptions_mapping; + string conversion_triggers_mapping; +} + +struct IssueEquityCompensationParams { + bytes16 id; + bytes16 stakeholder_id; + bytes16 stock_class_id; + bytes16 stock_plan_id; + uint256 quantity; + bytes16 security_id; + string compensation_type; + uint256 exercise_price; + uint256 base_price; + string expiration_date; + string custom_id; + string termination_exercise_windows_mapping; + string security_law_exemptions_mapping; +} + +struct IssueWarrantParams { + bytes16 id; + bytes16 stakeholder_id; + uint256 quantity; + bytes16 security_id; + uint256 purchase_price; + string custom_id; + string security_law_exemptions_mapping; + string exercise_triggers_mapping; +} + +struct StockConsolidationTx { + bytes16[] security_ids; + bytes16 resulting_security_id; +} + +struct StockTransferTx { + bytes16 consolidated_security_id; + bytes16 transferee_security_id; + bytes16 remainder_security_id; + uint256 quantity; + uint256 share_price; +} diff --git a/chain/src/libraries/TxHelper.sol b/chain/src/libraries/TxHelper.sol index 6aff4a01..0b6171ef 100644 --- a/chain/src/libraries/TxHelper.sol +++ b/chain/src/libraries/TxHelper.sol @@ -16,7 +16,8 @@ enum TxType { EQUITY_COMPENSATION_ISSUANCE, STOCK_PLAN_POOL_ADJUSTMENT, WARRANT_ISSUANCE, - EQUITY_COMPENSATION_EXERCISE + EQUITY_COMPENSATION_EXERCISE, + STOCK_CONSOLIDATION } struct Tx { diff --git a/chain/src/libraries/ValidationLib.sol b/chain/src/libraries/ValidationLib.sol index 97070c29..703d6e2e 100644 --- a/chain/src/libraries/ValidationLib.sol +++ b/chain/src/libraries/ValidationLib.sol @@ -48,7 +48,12 @@ library ValidationLib { uint256 stockClassIdx = ds.stockClassIndex[stock_class_id] - 1; StockClass storage stockClass = ds.stockClasses[stockClassIdx]; - require(ds.issuer.shares_issued + quantity <= ds.issuer.shares_authorized, "Issuer: Insufficient shares authorized"); - require(stockClass.shares_issued + quantity <= stockClass.shares_authorized, "StockClass: Insufficient shares authorized"); + require( + ds.issuer.shares_issued + quantity <= ds.issuer.shares_authorized, "Issuer: Insufficient shares authorized" + ); + require( + stockClass.shares_issued + quantity <= stockClass.shares_authorized, + "StockClass: Insufficient shares authorized" + ); } } diff --git a/chain/test/AccessControl.t.sol b/chain/test/AccessControl.t.sol index ffffe711..bcdd1afd 100644 --- a/chain/test/AccessControl.t.sol +++ b/chain/test/AccessControl.t.sol @@ -1,57 +1,306 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "./CapTable.t.sol"; - -contract RolesTests is CapTableTest { - address RANDO_ADDR = address(0xf001); - address OPERATOR_ADDR = address(0xf002); - - function testIssuerInitialization() public { - (bytes16 id, string memory legalName, , ) = capTable.issuer(); - assertEq(id, issuerId); - assertEq(legalName, "Winston, Inc."); - } - - function testOperatorTransfer() public { - bytes16[] memory stakeholderIds = new bytes16[](2); - - for (uint256 i = 0; i < 2; i++) { - bytes16 stakeholderId = bytes16(keccak256(abi.encodePacked("STAKEHOLDER", i))); - string memory stakeholderType = "Individual"; - string memory currentRelationship = "Investor"; - capTable.createStakeholder(stakeholderId, stakeholderType, currentRelationship); - stakeholderIds[i] = stakeholderId; - } - - bytes16 stockClassId = bytes16(keccak256(abi.encodePacked("STOCKCLASS"))); - string memory classType = "Common"; - uint256 pricePerShare = 100; // Example value - uint256 initialSharesAuthorized = 1000; // Example value - capTable.createStockClass(stockClassId, classType, pricePerShare, initialSharesAuthorized); - - // add operator and change address - capTable.addOperator(OPERATOR_ADDR); - vm.prank(OPERATOR_ADDR); - - vm.expectRevert("No active security ids found"); - - // will revert because we haven't performed an issuance, but it would have already verified operator - // role working - capTable.transferStock( - stakeholderIds[0], // transferor - stakeholderIds[1], // transferee - stockClassId, - true, - 100, - 100 +pragma solidity ^0.8.0; + +import "./TestBase.sol"; +import { AccessControl } from "@libraries/AccessControl.sol"; +import { IAccessControlFacet } from "@interfaces/IAccessControlFacet.sol"; +import { AccessControlFacet } from "@facets/AccessControlFacet.sol"; +import { StockClassFacet } from "@facets/StockClassFacet.sol"; +import { StockFacet } from "@facets/StockFacet.sol"; +import { EquityCompensationFacet } from "@facets/EquityCompensationFacet.sol"; +import { StakeholderNFTFacet } from "@facets/StakeholderNFTFacet.sol"; +import { StakeholderFacet } from "@facets/StakeholderFacet.sol"; +import { StockPlanFacet } from "@facets/StockPlanFacet.sol"; +import { IssueStockParams, IssueEquityCompensationParams } from "@libraries/Structs.sol"; + +contract AccessControlTest is DiamondTestBase { + address admin; + address operator; + address investor; + address unauthorized; + + function setUp() public override { + super.setUp(); + + // Set up test addresses + admin = address(0x1); + operator = address(0x2); + investor = address(0x3); + unauthorized = address(0x4); + + // contract owner is the FACTORY + vm.startPrank(contractOwner); + AccessControlFacet(address(capTable)).grantRole(AccessControl.DEFAULT_ADMIN_ROLE, admin); + AccessControlFacet(address(capTable)).transferAdmin(admin); + vm.stopPrank(); + + // Now have admin accept the role + vm.startPrank(admin); + AccessControlFacet(address(capTable)).acceptAdmin(); + AccessControlFacet(address(capTable)).grantRole(AccessControl.OPERATOR_ROLE, operator); + AccessControlFacet(address(capTable)).grantRole(AccessControl.INVESTOR_ROLE, investor); + vm.stopPrank(); + } + + function testStockClassFacetAccess() public { + // Test createStockClass + vm.startPrank(admin); + StockClassFacet(address(capTable)).createStockClass(bytes16(keccak256("stockClass1")), "Common", 100, 1000); + vm.stopPrank(); + + // Test unauthorized access + vm.startPrank(operator); + vm.expectRevert( + abi.encodeWithSelector( + AccessControl.AccessControlUnauthorized.selector, operator, AccessControl.DEFAULT_ADMIN_ROLE + ) + ); + StockClassFacet(address(capTable)).createStockClass(bytes16(keccak256("stockClass2")), "Preferred", 100, 1000); + vm.stopPrank(); + } + + function testStockFacetAccess() public { + // Create a stakeholder and stock class first + bytes16 stakeholderId = createStakeholder(); + bytes16 stockClassId = createStockClass(bytes16(uint128(2))); + bytes16 id1 = 0xd3373e0a4dd940000000000000000002; + bytes16 id2 = 0xd3373e0a4dd940000000000000000003; + + // Test issueStock with operator role + vm.startPrank(operator); + IssueStockParams memory params = IssueStockParams({ + id: id1, + stock_class_id: stockClassId, + share_price: 1, + quantity: 100, + stakeholder_id: stakeholderId, + security_id: bytes16(keccak256("security1")), + custom_id: "custom_id", + stock_legend_ids_mapping: "stock_legend_ids_mapping", + security_law_exemptions_mapping: "security_law_exemptions_mapping" + }); + StockFacet(address(capTable)).issueStock(params); + vm.stopPrank(); + + // Test unauthorized access + vm.startPrank(investor); + vm.expectRevert( + abi.encodeWithSelector( + AccessControl.AccessControlUnauthorized.selector, investor, AccessControl.OPERATOR_ROLE + ) + ); + IssueStockParams memory params2 = IssueStockParams({ + id: id2, + stock_class_id: stockClassId, + share_price: 1, + quantity: 100, + stakeholder_id: stakeholderId, + security_id: bytes16(keccak256("security2")), + custom_id: "custom_id", + stock_legend_ids_mapping: "stock_legend_ids_mapping", + security_law_exemptions_mapping: "security_law_exemptions_mapping" + }); + StockFacet(address(capTable)).issueStock(params2); + vm.stopPrank(); + } + + function testEquityCompensationFacetAccess() public { + // Create a stakeholder first + bytes16 stakeholderId = bytes16(keccak256("stakeholder1")); + vm.startPrank(contractOwner); + StakeholderFacet(address(capTable)).createStakeholder(stakeholderId); + vm.stopPrank(); + + // Create a stock class + bytes16 stockClassId = bytes16(keccak256("stockClass1")); + vm.startPrank(admin); + StockClassFacet(address(capTable)).createStockClass(stockClassId, "Common", 100, 1000); + vm.stopPrank(); + + // Create a stock plan + bytes16 stockPlanId = bytes16(keccak256("stockPlan1")); + bytes16[] memory stockClassIds = new bytes16[](1); + stockClassIds[0] = stockClassId; + vm.startPrank(admin); + StockPlanFacet(address(capTable)).createStockPlan(stockPlanId, stockClassIds, 1000); + vm.stopPrank(); + + bytes16 id1 = 0xd3373e0a4dd940000000000000000002; + bytes16 id2 = 0xd3373e0a4dd940000000000000000003; + + // Test issueEquityCompensation + vm.startPrank(operator); + IssueEquityCompensationParams memory params = IssueEquityCompensationParams({ + id: id1, + stakeholder_id: stakeholderId, + stock_class_id: stockClassId, + stock_plan_id: stockPlanId, + quantity: 100, + security_id: bytes16(keccak256("security1")), + compensation_type: "OPTION", + exercise_price: 100, + base_price: 100, + expiration_date: "2025-01-01", + custom_id: "custom_id", + termination_exercise_windows_mapping: "termination_exercise_windows_mapping", + security_law_exemptions_mapping: "security_law_exemptions_mapping" + }); + EquityCompensationFacet(address(capTable)).issueEquityCompensation(params); + vm.stopPrank(); + + // Test unauthorized access + vm.startPrank(investor); + vm.expectRevert( + abi.encodeWithSelector( + AccessControl.AccessControlUnauthorized.selector, investor, AccessControl.OPERATOR_ROLE + ) + ); + IssueEquityCompensationParams memory params2 = IssueEquityCompensationParams({ + id: id2, + stakeholder_id: stakeholderId, + stock_class_id: stockClassId, + stock_plan_id: stockPlanId, + quantity: 100, + security_id: bytes16(keccak256("security2")), + compensation_type: "OPTION", + exercise_price: 100, + base_price: 100, + expiration_date: "2025-01-01", + custom_id: "custom_id", + termination_exercise_windows_mapping: "termination_exercise_windows_mapping", + security_law_exemptions_mapping: "security_law_exemptions_mapping" + }); + EquityCompensationFacet(address(capTable)).issueEquityCompensation(params2); + vm.stopPrank(); + } + + function testStakeholderNFTFacetAccess() public { + // Create a stakeholder first and link it to the investor + bytes16 stakeholderId = bytes16(keccak256("stakeholder1")); + vm.startPrank(contractOwner); + StakeholderFacet(address(capTable)).createStakeholder(stakeholderId); + StakeholderFacet(address(capTable)).linkStakeholderAddress(stakeholderId, investor); + vm.stopPrank(); + + // Test mint + vm.startPrank(investor); + StakeholderNFTFacet(address(capTable)).mint(); + vm.stopPrank(); + + // Test unauthorized access + vm.startPrank(unauthorized); + vm.expectRevert( + abi.encodeWithSelector( + AccessControl.AccessControlUnauthorized.selector, unauthorized, AccessControl.INVESTOR_ROLE + ) ); + StakeholderNFTFacet(address(capTable)).mint(); + vm.stopPrank(); } - function testNotAdminReverting() public { - vm.prank(RANDO_ADDR); + function createStakeholder() public override returns (bytes16) { + bytes16 stakeholderId = bytes16(keccak256("stakeholder1")); + vm.startPrank(contractOwner); + StakeholderFacet(address(capTable)).createStakeholder(stakeholderId); + vm.stopPrank(); + return stakeholderId; + } + + function createStockClass(bytes16 stockClassId) public override returns (bytes16) { + vm.startPrank(admin); + StockClassFacet(address(capTable)).createStockClass(stockClassId, "Common", 100, 1000); + vm.stopPrank(); + return stockClassId; + } + + function testAdminTransfer() public { + address newAdmin = address(0x123); + + // Try transfer from non-admin (should fail) + vm.startPrank(unauthorized); + vm.expectRevert( + abi.encodeWithSelector( + AccessControl.AccessControlUnauthorized.selector, unauthorized, AccessControl.DEFAULT_ADMIN_ROLE + ) + ); + AccessControlFacet(address(capTable)).transferAdmin(newAdmin); + vm.stopPrank(); + + // Start admin transfer from current admin + vm.startPrank(admin); + AccessControlFacet(address(capTable)).transferAdmin(newAdmin); + + // Verify pending admin is set + assertEq(AccessControlFacet(address(capTable)).getPendingAdmin(), newAdmin); + vm.stopPrank(); + + // Try accept from wrong address (should fail) + vm.startPrank(unauthorized); + vm.expectRevert(IAccessControlFacet.AccessControlInvalidTransfer.selector); + AccessControlFacet(address(capTable)).acceptAdmin(); + vm.stopPrank(); + + // Accept transfer with new admin + vm.startPrank(newAdmin); + AccessControlFacet(address(capTable)).acceptAdmin(); + + // Verify new admin is set + assertEq(AccessControlFacet(address(capTable)).getAdmin(), newAdmin); + + // Verify old admin lost admin role + assertFalse(AccessControlFacet(address(capTable)).hasRole(AccessControl.DEFAULT_ADMIN_ROLE, admin)); + + // Verify new admin has admin role + assertTrue(AccessControlFacet(address(capTable)).hasRole(AccessControl.DEFAULT_ADMIN_ROLE, newAdmin)); + + // Verify new admin has operator and investor roles + assertTrue(AccessControlFacet(address(capTable)).hasRole(AccessControl.OPERATOR_ROLE, newAdmin)); + assertTrue(AccessControlFacet(address(capTable)).hasRole(AccessControl.INVESTOR_ROLE, newAdmin)); + vm.stopPrank(); + } + + function testCannotTransferToZeroAddress() public { + vm.startPrank(admin); + vm.expectRevert(IAccessControlFacet.AccessControlInvalidTransfer.selector); + AccessControlFacet(address(capTable)).transferAdmin(address(0)); + vm.stopPrank(); + } + + function testPendingAdminClearedAfterTransfer() public { + address newAdmin = address(0x123); + + // Start transfer + vm.startPrank(admin); + AccessControlFacet(address(capTable)).transferAdmin(newAdmin); + assertEq(AccessControlFacet(address(capTable)).getPendingAdmin(), newAdmin); + vm.stopPrank(); + + // Complete transfer + vm.startPrank(newAdmin); + AccessControlFacet(address(capTable)).acceptAdmin(); + + // Verify pending admin is cleared + assertEq(AccessControlFacet(address(capTable)).getPendingAdmin(), address(0)); + vm.stopPrank(); + } + + function testOnlyOneAdminAtATime() public { + address newAdmin = address(0x123); + + // Verify initial state + assertTrue(AccessControlFacet(address(capTable)).hasRole(AccessControl.DEFAULT_ADMIN_ROLE, admin)); + assertEq(AccessControlFacet(address(capTable)).getAdmin(), admin); + + // Complete transfer + vm.prank(admin); + AccessControlFacet(address(capTable)).transferAdmin(newAdmin); + + vm.prank(newAdmin); + AccessControlFacet(address(capTable)).acceptAdmin(); - vm.expectRevert("Does not have admin role"); - capTable.createStakeholder(bytes16("0101"), "Individual", "Investor"); + // Verify only new admin has admin role + assertFalse(AccessControlFacet(address(capTable)).hasRole(AccessControl.DEFAULT_ADMIN_ROLE, admin)); + assertTrue(AccessControlFacet(address(capTable)).hasRole(AccessControl.DEFAULT_ADMIN_ROLE, newAdmin)); + assertEq(AccessControlFacet(address(capTable)).getAdmin(), newAdmin); } } diff --git a/chain/test/Adjustment.t.sol b/chain/test/Adjustment.t.sol index 92fa5245..1b250e4e 100644 --- a/chain/test/Adjustment.t.sol +++ b/chain/test/Adjustment.t.sol @@ -3,6 +3,9 @@ pragma solidity ^0.8.0; import "./TestBase.sol"; import { TxHelper, TxType } from "@libraries/TxHelper.sol"; +import { IIssuerFacet } from "@interfaces/IIssuerFacet.sol"; +import { IStockClassFacet } from "@interfaces/IStockClassFacet.sol"; +import { IStockPlanFacet } from "@interfaces/IStockPlanFacet.sol"; contract DiamondAdjustmentTest is DiamondTestBase { bytes16 public stockClassId; @@ -10,59 +13,68 @@ contract DiamondAdjustmentTest is DiamondTestBase { function setUp() public override { super.setUp(); - stockClassId = createStockClass(); + stockClassId = createStockClass(bytes16(uint128(13))); bytes16[] memory stockClassIds = new bytes16[](1); stockClassIds[0] = stockClassId; stockPlanId = createStockPlan(stockClassIds); } function test_AdjustIssuerAuthorizedShares() public { - uint256 newSharesAuthorized = 2000000; - - // Expect both events in order - vm.expectEmit(true, false, false, true, address(capTable)); - emit IssuerAuthorizedSharesAdjusted(newSharesAuthorized); + bytes16 issuerAdjustmentId = bytes16(keccak256("ADJUSTMENT_1")); + uint256 newSharesAuthorized = 2_000_000; vm.expectEmit(true, true, false, true, address(capTable)); - emit TxHelper.TxCreated(TxType.ISSUER_AUTHORIZED_SHARES_ADJUSTMENT, abi.encode(newSharesAuthorized)); + emit TxHelper.TxCreated( + TxType.ISSUER_AUTHORIZED_SHARES_ADJUSTMENT, abi.encode(issuerAdjustmentId, issuerId, newSharesAuthorized) + ); - IssuerFacet(payable(address(capTable))).adjustIssuerAuthorizedShares(newSharesAuthorized); + IIssuerFacet(address(capTable)).adjustIssuerAuthorizedShares(issuerAdjustmentId, newSharesAuthorized); } function test_AdjustStockClassAuthorizedShares() public { - uint256 newSharesAuthorized = 2000000; + uint256 newSharesAuthorized = 2_000_000; + bytes16 stockClassAdjustmentId = bytes16(keccak256("ADJUSTMENT_1")); - vm.expectEmit(true, true, false, true, address(capTable)); - emit StockClassAuthorizedSharesAdjusted(stockClassId, newSharesAuthorized); + IIssuerFacet(address(capTable)).adjustIssuerAuthorizedShares(stockClassAdjustmentId, newSharesAuthorized); - vm.expectEmit(true, true, false, true, address(capTable)); - emit TxHelper.TxCreated(TxType.STOCK_CLASS_AUTHORIZED_SHARES_ADJUSTMENT, abi.encode(newSharesAuthorized)); + uint256 newStockClassSharesAuthorized = 1_999_999; - StockClassFacet(payable(address(capTable))).adjustAuthorizedShares(stockClassId, newSharesAuthorized); + IStockClassFacet(address(capTable)).adjustAuthorizedShares( + stockClassAdjustmentId, stockClassId, newStockClassSharesAuthorized + ); } function test_AdjustStockPlanPool() public { - uint256 newSharesReserved = 200000; + uint256 newSharesReserved = 200_000; + bytes16 stockPlanAdjustmentId = bytes16(keccak256("STOCK_PLAN_ADJ_1")); vm.expectEmit(true, true, false, true, address(capTable)); - emit TxHelper.TxCreated(TxType.STOCK_PLAN_POOL_ADJUSTMENT, abi.encode(newSharesReserved)); + emit TxHelper.TxCreated( + TxType.STOCK_PLAN_POOL_ADJUSTMENT, abi.encode(stockPlanAdjustmentId, stockPlanId, newSharesReserved) + ); - StockPlanFacet(payable(address(capTable))).adjustStockPlanPool(stockPlanId, newSharesReserved); + IStockPlanFacet(address(capTable)).adjustStockPlanPool(stockPlanAdjustmentId, stockPlanId, newSharesReserved); } function test_RevertWhen_AdjustingNonExistentStockClass() public { bytes16 invalidStockClassId = 0xd3373e0a4dd940000000000000000099; - uint256 newSharesAuthorized = 2000000; + bytes16 stockClassAdjustmentId = bytes16(keccak256("INVALID_ADJ_1")); + uint256 newSharesAuthorized = 2_000_000; - vm.expectRevert(abi.encodeWithSelector(StockClassFacet.StockClassNotFound.selector, invalidStockClassId)); - StockClassFacet(payable(address(capTable))).adjustAuthorizedShares(invalidStockClassId, newSharesAuthorized); + vm.expectRevert(abi.encodeWithSelector(IStockClassFacet.StockClassNotFound.selector, invalidStockClassId)); + IStockClassFacet(address(capTable)).adjustAuthorizedShares( + stockClassAdjustmentId, invalidStockClassId, newSharesAuthorized + ); } function test_RevertWhen_AdjustingNonExistentStockPlan() public { bytes16 invalidStockPlanId = 0xd3373e0a4dd940000000000000000099; - uint256 newSharesReserved = 200000; + bytes16 stockPlanAdjustmentId = bytes16(keccak256("INVALID_PLAN_ADJ_1")); + uint256 newSharesReserved = 200_000; - vm.expectRevert(abi.encodeWithSelector(StockPlanFacet.StockPlanNotFound.selector, invalidStockPlanId)); - StockPlanFacet(payable(address(capTable))).adjustStockPlanPool(invalidStockPlanId, newSharesReserved); + vm.expectRevert(abi.encodeWithSelector(IStockPlanFacet.StockPlanNotFound.selector, invalidStockPlanId)); + IStockPlanFacet(address(capTable)).adjustStockPlanPool( + stockPlanAdjustmentId, invalidStockPlanId, newSharesReserved + ); } } diff --git a/chain/test/CapTable.t.sol b/chain/test/CapTable.t.sol deleted file mode 100644 index ea89d5b3..00000000 --- a/chain/test/CapTable.t.sol +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "forge-std/Test.sol"; -import "../src/CapTable.sol"; -import "../src/CapTableFactory.sol"; -import { StockIssuanceParams } from "../src/lib/Structs.sol"; - -contract CapTableTest is Test { - CapTableFactory public factory; - CapTable public capTable; - uint256 public issuerInitialSharesAuthorized = 1000000; - string public issuerName = "Winston, Inc."; - bytes16 issuerId = 0xd3373e0a4dd9430f8a563281f2800e1e; - - function setUp() public { - CapTable capTableImplementation = new CapTable(); - - factory = new CapTableFactory(address(capTableImplementation)); - - capTable = CapTable(factory.createCapTable(issuerId, issuerName, issuerInitialSharesAuthorized)); - } - - // HELPERS // - - function createPranksterAndExpectRevert() public { - address prankster = address(0); - vm.prank(prankster); - vm.expectRevert("Does not have admin role"); - } - - function createStockClassAndStakeholder(uint256 stockClassInitialSharesAuthorized) public returns (bytes16, bytes16) { - bytes16 stakeholderId = 0xd3373e0a4dd940000000000000000005; - capTable.createStakeholder(stakeholderId, "INDIVIDUAL", "EMPLOYEE"); - - bytes16 stockClassId = 0xd3373e0a4dd940000000000000000000; - capTable.createStockClass(stockClassId, "COMMON", 100, stockClassInitialSharesAuthorized); - - return (stockClassId, stakeholderId); - } - - function issueStock(bytes16 stockClassId, bytes16 stakeholderId, uint256 quantity) public { - // Issue stock - StockIssuanceParams memory issuanceParams = StockIssuanceParams({ - stock_class_id: stockClassId, - stock_plan_id: 0x00000000000000000000000000000000, - share_numbers_issued: ShareNumbersIssued(0, 0), - share_price: 100, - quantity: quantity, - vesting_terms_id: 0x00000000000000000000000000000000, - cost_basis: 50, - stock_legend_ids: new bytes16[](0), - issuance_type: "RSA", - comments: new string[](0), - custom_id: "R2-D2", - stakeholder_id: stakeholderId, - board_approval_date: "2023-01-01", - stockholder_approval_date: "2023-01-02", - consideration_text: "For services rendered", - security_law_exemptions: new string[](0) - }); - capTable.issueStock(issuanceParams); - } -} diff --git a/chain/test/CapTableFactory.sol b/chain/test/CapTableFactory.sol deleted file mode 100644 index 9f8463f2..00000000 --- a/chain/test/CapTableFactory.sol +++ /dev/null @@ -1,114 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "forge-std/Test.sol"; -import "../src/CapTableFactory.sol"; -import "../src/CapTable.sol"; -import "../src/interfaces/ICapTable.sol"; - -contract CapTableFactoryTest is Test { - CapTableFactory private _capTableFactory; - CapTable private _capTableImplementation; - - function setUp() public { - // Deploy empty implementation - _capTableImplementation = new CapTable(); - - _capTableFactory = new CapTableFactory(address(_capTableImplementation)); - } - - function testRevertsInvalidImplementationAddress() public { - vm.expectRevert("Invalid implementation address"); - _capTableFactory = new CapTableFactory(address(0)); - } - - function testCreateCapTable() public { - bytes16 issuerId = 0xd3373e0a4dd9430f8a563281f2800e1e; - string memory issuerName = "Winston, Inc."; - uint256 issuerInitialSharesAuthorized = 1000000; - - address capTableProxy = _capTableFactory.createCapTable(issuerId, issuerName, issuerInitialSharesAuthorized); - - // Assert the cap table was created - CapTable capTable = CapTable(capTableProxy); - - (bytes16 id, string memory name, uint256 shares_issued, uint256 initial_shares_authorized) = capTable.issuer(); - - assertEq(id, issuerId); - assertEq(name, issuerName); - assertEq(shares_issued, 0); - assertEq(initial_shares_authorized, issuerInitialSharesAuthorized); - - // Verify by creating a stakeholder - bytes16 stakeholderId = 0xd3373e0a4dd940000000000000000010; - string memory stakeholderType = "INDIVIDUAL"; - string memory stakeholderRelationship = "INVESTOR"; - - capTable.createStakeholder(stakeholderId, stakeholderType, stakeholderRelationship); - - (bytes16 actualStakeolderId, string memory actualStakeholderType, string memory actualStakeholderRelationship) = capTable.getStakeholderById( - stakeholderId - ); - - assertEq(actualStakeolderId, stakeholderId); - assertEq(actualStakeholderType, stakeholderType); - assertEq(actualStakeholderRelationship, stakeholderRelationship); - - // Testing that only owner can create stakeholder - vm.prank(address(1)); - vm.expectRevert("Does not have admin role"); - capTable.createStakeholder(0xd3373e0a4dd940000000000000000005, "INDIVIDUAL", "EMPLOYEE"); - } - - function testUpdateCapTableImplementation() public { - // Create cap table prior to upgrade - bytes16 issuerId0 = 0xd3373e0a4dd9430f8a563281f2800333; - string memory issuerName0 = "Alpha, Inc."; - uint256 issuerInitialSharesAuthorized0 = 1000000; - - address capTableProxy0 = _capTableFactory.createCapTable(issuerId0, issuerName0, issuerInitialSharesAuthorized0); - - // Assert the cap table was created - CapTable capTable0 = CapTable(capTableProxy0); - - // Deploy new implementation - CapTable newCapTableImplementation = new CapTable(); - - // Update implementation - _capTableFactory.updateCapTableImplementation(address(newCapTableImplementation)); - - // Assert the implementation was updated - assertEq(address(_capTableFactory.capTableImplementation()), address(newCapTableImplementation)); - assertEq(address(_capTableFactory.capTableBeacon().implementation()), address(newCapTableImplementation)); - - // Create a cap table - bytes16 issuerId = 0xd3373e0a4dd9430f8a563281f2800e1e; - string memory issuerName = "Winston, Inc."; - uint256 issuerInitialSharesAuthorized = 1000000; - - address capTableProxy = _capTableFactory.createCapTable(issuerId, issuerName, issuerInitialSharesAuthorized); - - // Assert the cap table was created - CapTable capTable = CapTable(capTableProxy); - - (bytes16 id, string memory name, uint256 shares_issued, uint256 initial_shares_authorized) = capTable.issuer(); - - assertEq(id, issuerId); - assertEq(name, issuerName); - assertEq(shares_issued, 0); - assertEq(initial_shares_authorized, issuerInitialSharesAuthorized); - - // make sure previous cap table still works - bytes16 stakeholderId = 0xd3373e0a4dd940000000000000000010; - - capTable0.createStakeholder(stakeholderId, "INDIVIDUAL", "INVESTOR"); - - (bytes16 actualStakeolderId, string memory actualStakeholderType, string memory actualStakeholderRelationship) = capTable0.getStakeholderById( - stakeholderId - ); - - assertEq(actualStakeolderId, stakeholderId); - assertEq(actualStakeholderType, "INDIVIDUAL"); - assertEq(actualStakeholderRelationship, "INVESTOR"); - } -} diff --git a/chain/test/ConvertibleIssuance.t.sol b/chain/test/ConvertibleIssuance.t.sol index 034166a8..7eca7467 100644 --- a/chain/test/ConvertibleIssuance.t.sol +++ b/chain/test/ConvertibleIssuance.t.sol @@ -5,36 +5,76 @@ import "./TestBase.sol"; import { StorageLib } from "@core/Storage.sol"; import { TxHelper, TxType } from "@libraries/TxHelper.sol"; import { ValidationLib } from "@libraries/ValidationLib.sol"; -import { ConvertibleActivePosition } from "@libraries/Structs.sol"; +import { ConvertibleActivePosition, IssueConvertibleParams } from "@libraries/Structs.sol"; +import { IConvertiblesFacet } from "@interfaces/IConvertiblesFacet.sol"; contract DiamondConvertibleIssuanceTest is DiamondTestBase { function testIssueConvertible() public { bytes16 stakeholderId = createStakeholder(); - uint256 investmentAmount = 1000000; + uint256 investmentAmount = 1_000_000; bytes16 securityId = 0xd3373e0a4dd940000000000000000001; + bytes16 id = 0xd3373e0a4dd940000000000000000002; + IssueConvertibleParams memory params = IssueConvertibleParams({ + id: id, + stakeholder_id: stakeholderId, + investment_amount: investmentAmount, + security_id: securityId, + convertible_type: "SAFE", + seniority: 1, + custom_id: "CONV_001", + security_law_exemptions_mapping: "REG_D", + conversion_triggers_mapping: "CONVERSION_ON_NEXT_EQUITY" + }); vm.expectEmit(true, true, false, true, address(capTable)); - emit TxHelper.TxCreated(TxType.CONVERTIBLE_ISSUANCE, abi.encode(stakeholderId, investmentAmount, securityId)); + emit TxHelper.TxCreated(TxType.CONVERTIBLE_ISSUANCE, abi.encode(params)); - ConvertiblesFacet(address(capTable)).issueConvertible(stakeholderId, investmentAmount, securityId); + IConvertiblesFacet(address(capTable)).issueConvertible(params); // Verify position was created correctly - ConvertibleActivePosition memory position = ConvertiblesFacet(address(capTable)).getConvertiblePosition(securityId); + ConvertibleActivePosition memory position = + IConvertiblesFacet(address(capTable)).getConvertiblePosition(securityId); assertEq(position.investment_amount, investmentAmount); assertEq(position.stakeholder_id, stakeholderId); } - function testFailInvalidStakeholder() public { + function test_RevertInvalidStakeholder() public { bytes16 invalidStakeholderId = 0xd3373e0a4dd940000000000000000099; bytes16 securityId = 0xd3373e0a4dd940000000000000000001; + bytes16 id = 0xd3373e0a4dd940000000000000000002; - ConvertiblesFacet(address(capTable)).issueConvertible(invalidStakeholderId, 1000000, securityId); + IssueConvertibleParams memory params = IssueConvertibleParams({ + id: id, + stakeholder_id: invalidStakeholderId, + investment_amount: 1_000_000, + security_id: securityId, + convertible_type: "SAFE", + seniority: 1, + custom_id: "CONV_002", + security_law_exemptions_mapping: "REG_D", + conversion_triggers_mapping: "CONVERSION_ON_NEXT_EQUITY" + }); + vm.expectRevert(abi.encodeWithSignature("NoStakeholder(bytes16)", invalidStakeholderId)); + IConvertiblesFacet(address(capTable)).issueConvertible(params); } - function testFailZeroAmount() public { + function test_RevertZeroAmount() public { bytes16 stakeholderId = createStakeholder(); bytes16 securityId = 0xd3373e0a4dd940000000000000000001; + bytes16 id = 0xd3373e0a4dd940000000000000000002; - ConvertiblesFacet(address(capTable)).issueConvertible(stakeholderId, 0, securityId); + IssueConvertibleParams memory params = IssueConvertibleParams({ + id: id, + stakeholder_id: stakeholderId, + investment_amount: 0, + security_id: securityId, + convertible_type: "SAFE", + seniority: 1, + custom_id: "CONV_003", + security_law_exemptions_mapping: "REG_D", + conversion_triggers_mapping: "CONVERSION_ON_NEXT_EQUITY" + }); + vm.expectRevert(abi.encodeWithSignature("InvalidAmount()")); + IConvertiblesFacet(address(capTable)).issueConvertible(params); } } diff --git a/chain/test/EquityCompExercise.t.sol b/chain/test/EquityCompExercise.t.sol index 1f45b806..216c033a 100644 --- a/chain/test/EquityCompExercise.t.sol +++ b/chain/test/EquityCompExercise.t.sol @@ -1,11 +1,21 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +import { Test } from "forge-std/Test.sol"; import "./TestBase.sol"; import { StorageLib } from "@core/Storage.sol"; import { TxHelper, TxType } from "@libraries/TxHelper.sol"; import { ValidationLib } from "@libraries/ValidationLib.sol"; -import { EquityCompensationActivePosition, StockActivePosition } from "@libraries/Structs.sol"; +import { + EquityCompensationActivePosition, + StockActivePosition, + IssueEquityCompensationParams, + IssueStockParams +} from "@libraries/Structs.sol"; +import { AccessControl } from "@libraries/AccessControl.sol"; +import { IAccessControlFacet } from "@interfaces/IAccessControlFacet.sol"; +import { IStockFacet } from "@interfaces/IStockFacet.sol"; +import { IEquityCompensationFacet } from "@interfaces/IEquityCompensationFacet.sol"; contract DiamondEquityCompExerciseTest is DiamondTestBase { bytes16 stakeholderId; @@ -14,13 +24,26 @@ contract DiamondEquityCompExerciseTest is DiamondTestBase { bytes16 equityCompSecurityId; bytes16 stockSecurityId; uint256 constant EQUITY_COMP_QUANTITY = 1000; + address stakeholderWallet; function setUp() public override { super.setUp(); + // Grant necessary roles + vm.startPrank(contractOwner); + IAccessControlFacet(address(capTable)).grantRole(AccessControl.OPERATOR_ROLE, address(this)); + vm.stopPrank(); + // Create prerequisites stakeholderId = createStakeholder(); - stockClassId = createStockClass(); + stakeholderWallet = address(0xF62849F9A0B5Bf2913b396098F7c7019b51A820a); + linkStakeholderAddress(stakeholderId, stakeholderWallet); + + // Grant investor role to stakeholder + vm.prank(contractOwner); + IAccessControlFacet(address(capTable)).grantRole(AccessControl.INVESTOR_ROLE, stakeholderWallet); + + stockClassId = createStockClass(bytes16(uint128(11))); bytes16[] memory stockClassIds = new bytes16[](1); stockClassIds[0] = stockClassId; @@ -28,90 +51,175 @@ contract DiamondEquityCompExerciseTest is DiamondTestBase { // Issue equity compensation equityCompSecurityId = 0xd3373e0a4dd940000000000000000001; - EquityCompensationFacet(address(capTable)).issueEquityCompensation( - stakeholderId, - stockClassId, - stockPlanId, - EQUITY_COMP_QUANTITY, - equityCompSecurityId - ); + bytes16 equityCompensationId = 0xd3373e0a4dd940000000000000000012; + IssueEquityCompensationParams memory equityParams = IssueEquityCompensationParams({ + id: equityCompensationId, + stakeholder_id: stakeholderId, + stock_class_id: stockClassId, + stock_plan_id: stockPlanId, + quantity: EQUITY_COMP_QUANTITY, + security_id: equityCompSecurityId, + compensation_type: "ISO", + exercise_price: 1e18, + base_price: 1e18, + expiration_date: "2025-12-31", + custom_id: "EQCOMP_EX_001", + termination_exercise_windows_mapping: "90_DAYS", + security_law_exemptions_mapping: "REG_D" + }); + IEquityCompensationFacet(address(capTable)).issueEquityCompensation(equityParams); // Issue resulting stock stockSecurityId = 0xd3373e0a4dd940000000000000000002; - StockFacet(address(capTable)).issueStock( - stockClassId, - 1e18, // share price - EQUITY_COMP_QUANTITY, - stakeholderId, - stockSecurityId - ); + bytes16 stockId = 0xd3373e0a4dd940000000000000000011; + IssueStockParams memory params = IssueStockParams({ + id: stockId, + stock_class_id: stockClassId, + share_price: 1e18, + quantity: EQUITY_COMP_QUANTITY, + stakeholder_id: stakeholderId, + security_id: stockSecurityId, + custom_id: "STOCK_EX_001", + stock_legend_ids_mapping: "LEGEND_1", + security_law_exemptions_mapping: "REG_D" + }); + IStockFacet(address(capTable)).issueStock(params); } function testExerciseEquityCompensation() public { uint256 exerciseQuantity = 500; + bytes16 exerciseId = 0xd3373e0a4dd940000000000000000113; // Issue new stock position with exact quantity to exercise bytes16 newStockSecurityId = 0xd3373e0a4dd940000000000000000003; - StockFacet(address(capTable)).issueStock( - stockClassId, - 1e18, // share price - exerciseQuantity, // Must match exercise quantity - stakeholderId, - newStockSecurityId - ); + bytes16 newStockId = 0xd3373e0a4dd940000000000000000013; + IssueStockParams memory exerciseParams = IssueStockParams({ + id: newStockId, + stock_class_id: stockClassId, + share_price: 1e18, + quantity: exerciseQuantity, + stakeholder_id: stakeholderId, + security_id: newStockSecurityId, + custom_id: "STOCK_EX_002", + stock_legend_ids_mapping: "LEGEND_1", + security_law_exemptions_mapping: "REG_D" + }); + IStockFacet(address(capTable)).issueStock(exerciseParams); vm.expectEmit(true, true, false, true, address(capTable)); - emit TxHelper.TxCreated(TxType.EQUITY_COMPENSATION_EXERCISE, abi.encode(equityCompSecurityId, newStockSecurityId, exerciseQuantity)); + emit TxHelper.TxCreated( + TxType.EQUITY_COMPENSATION_EXERCISE, + abi.encode(exerciseId, equityCompSecurityId, newStockSecurityId, exerciseQuantity) + ); - EquityCompensationFacet(address(capTable)).exerciseEquityCompensation(equityCompSecurityId, newStockSecurityId, exerciseQuantity); + // Exercise as operator + IEquityCompensationFacet(address(capTable)).exerciseEquityCompensation( + exerciseId, equityCompSecurityId, newStockSecurityId, exerciseQuantity + ); // Verify equity comp position was updated - EquityCompensationActivePosition memory position = EquityCompensationFacet(address(capTable)).getPosition(equityCompSecurityId); + EquityCompensationActivePosition memory position = + IEquityCompensationFacet(address(capTable)).getPosition(equityCompSecurityId); assertEq(position.quantity, EQUITY_COMP_QUANTITY - exerciseQuantity); } function testExerciseEquityCompensationFull() public { + bytes16 exerciseId = bytes16(keccak256("EXERCISE_FULL")); + vm.expectEmit(true, true, false, true, address(capTable)); - emit TxHelper.TxCreated(TxType.EQUITY_COMPENSATION_EXERCISE, abi.encode(equityCompSecurityId, stockSecurityId, EQUITY_COMP_QUANTITY)); + emit TxHelper.TxCreated( + TxType.EQUITY_COMPENSATION_EXERCISE, + abi.encode(exerciseId, equityCompSecurityId, stockSecurityId, EQUITY_COMP_QUANTITY) + ); - EquityCompensationFacet(address(capTable)).exerciseEquityCompensation(equityCompSecurityId, stockSecurityId, EQUITY_COMP_QUANTITY); + // Exercise as operator + IEquityCompensationFacet(address(capTable)).exerciseEquityCompensation( + exerciseId, equityCompSecurityId, stockSecurityId, EQUITY_COMP_QUANTITY + ); // Verify position was removed - EquityCompensationActivePosition memory position = EquityCompensationFacet(address(capTable)).getPosition(equityCompSecurityId); + EquityCompensationActivePosition memory position = + IEquityCompensationFacet(address(capTable)).getPosition(equityCompSecurityId); assertEq(position.quantity, 0); } - function testFailInvalidEquityCompSecurity() public { + function test_RevertNonOperatorExercise() public { + address nonOperator = address(0x129); + vm.prank(nonOperator); + bytes16 exerciseId = bytes16(keccak256("NON_OPERATOR")); + vm.expectRevert( + abi.encodeWithSignature( + "AccessControlUnauthorized(address,bytes32)", nonOperator, AccessControl.OPERATOR_ROLE + ) + ); + IEquityCompensationFacet(address(capTable)).exerciseEquityCompensation( + exerciseId, equityCompSecurityId, stockSecurityId, EQUITY_COMP_QUANTITY + ); + } + + function test_RevertInvalidEquityCompSecurity() public { bytes16 invalidSecurityId = 0xd3373e0a4dd940000000000000000099; + bytes16 exerciseId = bytes16(keccak256("INVALID_EXERCISE_1")); - EquityCompensationFacet(address(capTable)).exerciseEquityCompensation(invalidSecurityId, stockSecurityId, 500); + vm.expectRevert(abi.encodeWithSignature("InvalidSecurity(bytes16)", invalidSecurityId)); + IEquityCompensationFacet(address(capTable)).exerciseEquityCompensation( + exerciseId, invalidSecurityId, stockSecurityId, 500 + ); } - function testFailInvalidStockSecurity() public { + function test_RevertInvalidStockSecurity() public { bytes16 invalidStockId = 0xd3373e0a4dd940000000000000000099; + bytes16 exerciseId = bytes16(keccak256("INVALID_EXERCISE_2")); - EquityCompensationFacet(address(capTable)).exerciseEquityCompensation(equityCompSecurityId, invalidStockId, 500); + vm.expectRevert(abi.encodeWithSignature("InvalidSecurity(bytes16)", invalidStockId)); + IEquityCompensationFacet(address(capTable)).exerciseEquityCompensation( + exerciseId, equityCompSecurityId, invalidStockId, 500 + ); } - function testFailInsufficientShares() public { - EquityCompensationFacet(address(capTable)).exerciseEquityCompensation(equityCompSecurityId, stockSecurityId, EQUITY_COMP_QUANTITY + 1); + function test_RevertInsufficientShares() public { + bytes16 exerciseId = bytes16(keccak256("INSUFFICIENT_SHARES")); + + vm.expectRevert(abi.encodeWithSignature("InsufficientShares()")); + IEquityCompensationFacet(address(capTable)).exerciseEquityCompensation( + exerciseId, equityCompSecurityId, stockSecurityId, EQUITY_COMP_QUANTITY + 1 + ); } - function testFailWrongStakeholder() public { + function test_RevertWrongStakeholder() public { // Create a different stakeholder with unique ID - bytes16 otherStakeholderId = createStakeholder(); + bytes16 otherStakeholderId = bytes16(uint128(uint256(keccak256("WRONG_STAKEHOLDER")))); + bytes16 exerciseId = bytes16(keccak256("WRONG_STAKEHOLDER")); + IStakeholderFacet(address(capTable)).createStakeholder(otherStakeholderId); // Issue stock to different stakeholder bytes16 otherStockSecurityId = 0xd3373e0a4dd940000000000000000003; - StockFacet(address(capTable)).issueStock( - stockClassId, - 1e18, // share price - 500, - otherStakeholderId, - otherStockSecurityId + bytes16 otherStockId = 0xd3373e0a4dd940000000000000000013; + IssueStockParams memory otherParams = IssueStockParams({ + id: otherStockId, + stock_class_id: stockClassId, + share_price: 1e18, + quantity: 500, + stakeholder_id: otherStakeholderId, + security_id: otherStockSecurityId, + custom_id: "STOCK_EX_003", + stock_legend_ids_mapping: "LEGEND_1", + security_law_exemptions_mapping: "REG_D" + }); + IStockFacet(address(capTable)).issueStock(otherParams); + + // Get the equity compensation position to get the stakeholder ID + EquityCompensationActivePosition memory position = + IEquityCompensationFacet(address(capTable)).getPosition(equityCompSecurityId); + bytes16 equityCompStakeholderId = position.stakeholder_id; + + vm.expectRevert( + abi.encodeWithSignature( + "InvalidSecurityStakeholder(bytes16,bytes16)", otherStockSecurityId, equityCompStakeholderId + ) + ); + IEquityCompensationFacet(address(capTable)).exerciseEquityCompensation( + exerciseId, equityCompSecurityId, otherStockSecurityId, 500 ); - - vm.expectRevert(abi.encodeWithSelector(ValidationLib.InvalidSecurityStakeholder.selector, otherStockSecurityId, stakeholderId)); - EquityCompensationFacet(address(capTable)).exerciseEquityCompensation(equityCompSecurityId, otherStockSecurityId, 500); } } diff --git a/chain/test/EquityCompensationIssuance.t.sol b/chain/test/EquityCompensationIssuance.t.sol index ba9e0c39..70c881c0 100644 --- a/chain/test/EquityCompensationIssuance.t.sol +++ b/chain/test/EquityCompensationIssuance.t.sol @@ -5,7 +5,9 @@ import "./TestBase.sol"; import { StorageLib } from "@core/Storage.sol"; import { TxHelper, TxType } from "@libraries/TxHelper.sol"; import { ValidationLib } from "@libraries/ValidationLib.sol"; -import { EquityCompensationActivePosition } from "@libraries/Structs.sol"; +import { EquityCompensationActivePosition, IssueEquityCompensationParams } from "@libraries/Structs.sol"; +import { AccessControl } from "@libraries/AccessControl.sol"; +import { IEquityCompensationFacet } from "@interfaces/IEquityCompensationFacet.sol"; contract DiamondEquityCompensationIssuanceTest is DiamondTestBase { bytes16 stakeholderId; @@ -14,8 +16,14 @@ contract DiamondEquityCompensationIssuanceTest is DiamondTestBase { function setUp() public override { super.setUp(); + + // Grant necessary roles + vm.startPrank(contractOwner); + IAccessControlFacet(address(capTable)).grantRole(AccessControl.OPERATOR_ROLE, address(this)); + vm.stopPrank(); + stakeholderId = createStakeholder(); - stockClassId = createStockClass(); + stockClassId = createStockClass(bytes16(uint128(10))); // Create array properly bytes16[] memory stockClassIds = new bytes16[](1); @@ -26,44 +34,105 @@ contract DiamondEquityCompensationIssuanceTest is DiamondTestBase { function testIssueEquityCompensation() public { uint256 quantity = 1000; bytes16 securityId = 0xd3373e0a4dd940000000000000000001; + bytes16 id = 0xd3373e0a4dd940000000000000000002; + IssueEquityCompensationParams memory params = IssueEquityCompensationParams({ + id: id, + stakeholder_id: stakeholderId, + stock_class_id: stockClassId, + stock_plan_id: stockPlanId, + quantity: quantity, + security_id: securityId, + compensation_type: "ISO", + exercise_price: 1e18, + base_price: 1e18, + expiration_date: "2025-12-31", + custom_id: "EQCOMP_001", + termination_exercise_windows_mapping: "90_DAYS", + security_law_exemptions_mapping: "REG_D" + }); vm.expectEmit(true, true, false, true, address(capTable)); - emit TxHelper.TxCreated(TxType.EQUITY_COMPENSATION_ISSUANCE, abi.encode(stakeholderId, stockClassId, stockPlanId, quantity, securityId)); + emit TxHelper.TxCreated(TxType.EQUITY_COMPENSATION_ISSUANCE, abi.encode(params)); - EquityCompensationFacet(address(capTable)).issueEquityCompensation(stakeholderId, stockClassId, stockPlanId, quantity, securityId); + IEquityCompensationFacet(address(capTable)).issueEquityCompensation(params); // Verify position was created correctly - EquityCompensationActivePosition memory position = EquityCompensationFacet(address(capTable)).getPosition(securityId); + EquityCompensationActivePosition memory position = + IEquityCompensationFacet(address(capTable)).getPosition(securityId); assertEq(position.quantity, quantity); assertEq(position.stakeholder_id, stakeholderId); assertEq(position.stock_class_id, stockClassId); assertEq(position.stock_plan_id, stockPlanId); } - function testFailInvalidStakeholder() public { + function test_RevertInvalidStakeholder() public { bytes16 invalidStakeholderId = 0xd3373e0a4dd940000000000000000099; bytes16 securityId = 0xd3373e0a4dd940000000000000000001; - - EquityCompensationFacet(address(capTable)).issueEquityCompensation(invalidStakeholderId, stockClassId, stockPlanId, 1000, securityId); + bytes16 id = 0xd3373e0a4dd940000000000000000002; + + IssueEquityCompensationParams memory params = IssueEquityCompensationParams({ + id: id, + stakeholder_id: invalidStakeholderId, + stock_class_id: stockClassId, + stock_plan_id: stockPlanId, + quantity: 1000, + security_id: securityId, + compensation_type: "ISO", + exercise_price: 1e18, + base_price: 1e18, + expiration_date: "2025-12-31", + custom_id: "EQCOMP_002", + termination_exercise_windows_mapping: "90_DAYS", + security_law_exemptions_mapping: "REG_D" + }); + vm.expectRevert(abi.encodeWithSignature("NoStakeholder(bytes16)", invalidStakeholderId)); + IEquityCompensationFacet(address(capTable)).issueEquityCompensation(params); } - function testFailInvalidStockClass() public { + function test_RevertInvalidStockClass() public { bytes16 invalidStockClassId = 0xd3373e0a4dd940000000000000000099; bytes16 securityId = 0xd3373e0a4dd940000000000000000001; - - EquityCompensationFacet(address(capTable)).issueEquityCompensation(stakeholderId, invalidStockClassId, stockPlanId, 1000, securityId); + bytes16 id = 0xd3373e0a4dd940000000000000000002; + + IssueEquityCompensationParams memory params = IssueEquityCompensationParams({ + id: id, + stakeholder_id: stakeholderId, + stock_class_id: invalidStockClassId, + stock_plan_id: stockPlanId, + quantity: 1000, + security_id: securityId, + compensation_type: "ISO", + exercise_price: 1e18, + base_price: 1e18, + expiration_date: "2025-12-31", + custom_id: "EQCOMP_003", + termination_exercise_windows_mapping: "90_DAYS", + security_law_exemptions_mapping: "REG_D" + }); + vm.expectRevert(abi.encodeWithSignature("InvalidStockClass(bytes16)", invalidStockClassId)); + IEquityCompensationFacet(address(capTable)).issueEquityCompensation(params); } - function testFailInvalidStockPlan() public { - bytes16 invalidStockPlanId = 0xd3373e0a4dd940000000000000000099; + function test_RevertZeroQuantity() public { bytes16 securityId = 0xd3373e0a4dd940000000000000000001; - - EquityCompensationFacet(address(capTable)).issueEquityCompensation(stakeholderId, stockClassId, invalidStockPlanId, 1000, securityId); - } - - function testFailZeroQuantity() public { - bytes16 securityId = 0xd3373e0a4dd940000000000000000001; - - EquityCompensationFacet(address(capTable)).issueEquityCompensation(stakeholderId, stockClassId, stockPlanId, 0, securityId); + bytes16 id = 0xd3373e0a4dd940000000000000000002; + + IssueEquityCompensationParams memory params = IssueEquityCompensationParams({ + id: id, + stakeholder_id: stakeholderId, + stock_class_id: stockClassId, + stock_plan_id: stockPlanId, + quantity: 0, + security_id: securityId, + compensation_type: "ISO", + exercise_price: 1e18, + base_price: 1e18, + expiration_date: "2025-12-31", + custom_id: "EQCOMP_005", + termination_exercise_windows_mapping: "90_DAYS", + security_law_exemptions_mapping: "REG_D" + }); + vm.expectRevert(abi.encodeWithSignature("InvalidQuantity()")); + IEquityCompensationFacet(address(capTable)).issueEquityCompensation(params); } } diff --git a/chain/test/Issuer.t.sol b/chain/test/Issuer.t.sol deleted file mode 100644 index 0e95a8bf..00000000 --- a/chain/test/Issuer.t.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "./CapTable.t.sol"; - -contract IssuerTest is CapTableTest { - function testIssuerInitialization() public { - (bytes16 id, string memory legal_name, uint256 shares_issued, uint256 shares_authorized) = capTable.issuer(); - bytes16 expectedId = 0xd3373e0a4dd9430f8a563281f2800e1e; - assertEq(id, expectedId); - assertEq(legal_name, "Winston, Inc."); - assertEq(shares_authorized, issuerInitialSharesAuthorized); - assertEq(shares_issued, 0); - assertNotEq(shares_issued, 1); - } -} diff --git a/chain/test/Seeding.t.sol b/chain/test/Seeding.t.sol deleted file mode 100644 index c5023c04..00000000 --- a/chain/test/Seeding.t.sol +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "./CapTable.t.sol"; -import { InitialShares, IssuerInitialShares, StockClassInitialShares } from "../src/lib/Structs.sol"; - -contract SeedingTest is CapTableTest { - function createInitialDummyStockClassData() private pure returns (bytes16, string memory, uint256, uint256) { - bytes16 id = 0xd3373e0a4dd9430f8a563281f2454545; - string memory classType = "Common"; - uint256 pricePerShare = 10000000000; // $1.00 with 10 decimals - uint256 initialSharesAuthorized = 100000000000000000; // 10,000,000 - return (id, classType, pricePerShare, initialSharesAuthorized); - } - - function testValidSeedingOfShares() public { - (bytes16 stockClassId, string memory classType, uint256 pricePerShare, uint256 initialSharesAuthorized) = createInitialDummyStockClassData(); - capTable.createStockClass(stockClassId, classType, pricePerShare, initialSharesAuthorized); - - uint256 expectedIssuerSharesAuthorized = 1000000000000000000; // 100M - uint256 expectedIssuerSharesIssued = 350000000000000000; // 35M - uint256 expectedStockClassSharesAuthorized = 1000000000000000000; // 100M - uint256 expectedStockClassSharesIssued = 350000000000000000; // 35M - - StockClassInitialShares[] memory stockClassInitialShares = new StockClassInitialShares[](1); - stockClassInitialShares[0] = StockClassInitialShares(stockClassId, expectedStockClassSharesAuthorized, expectedStockClassSharesIssued); - - InitialShares memory params = InitialShares( - IssuerInitialShares(expectedIssuerSharesAuthorized, expectedIssuerSharesIssued), - stockClassInitialShares - ); - - capTable.seedSharesAuthorizedAndIssued(params); - - (, , uint256 actualIssuerSharesIssued, uint256 actualIssuerSharesAuthorized) = capTable.issuer(); - (, , , uint256 scSharesIssued, uint256 scSharesAuthorized) = capTable.getStockClassById(stockClassId); - - assertEq(actualIssuerSharesAuthorized, expectedIssuerSharesAuthorized); - assertEq(actualIssuerSharesIssued, expectedIssuerSharesIssued); - assertEq(scSharesAuthorized, expectedStockClassSharesAuthorized); - assertEq(scSharesIssued, expectedStockClassSharesIssued); - } - - function testSeedingWithInvalidParameters() public { - // Attempt to seed with zero shares authorized and issued - InitialShares memory params = InitialShares(IssuerInitialShares(0, 0), new StockClassInitialShares[](0)); - - vm.expectRevert("Invalid Seeding Shares Params"); - capTable.seedSharesAuthorizedAndIssued(params); - } - - function testSeedMultipleActivePositionsAndSecurityIds() public { - bytes16[] memory stakeholderIds = new bytes16[](5); - bytes16[] memory securityIds = new bytes16[](5); - bytes16[] memory stockClassIds = new bytes16[](5); - uint256[] memory quantities = new uint256[](5); - uint256[] memory sharePrices = new uint256[](5); - uint40[] memory timestamps = new uint40[](5); - - for (uint256 i = 0; i < 5; i++) { - // Generate unique identifiers for stock classes and stakeholders - bytes16 stockClassId = bytes16(keccak256(abi.encodePacked("STOCKCLASS", i))); - bytes16 stakeholderId = bytes16(keccak256(abi.encodePacked("STAKEHOLDER", i))); - bytes16 securityId = bytes16(keccak256(abi.encodePacked("SECURITY", i))); - - // Create stock classes and stakeholders - capTable.createStockClass(stockClassId, "Common", 10000000000, 100000000000000000); - capTable.createStakeholder(stakeholderId, "INDIVIDUAL", "INVESTOR"); - - stakeholderIds[i] = stakeholderId; - securityIds[i] = securityId; // Dummy security IDs - stockClassIds[i] = stockClassId; - quantities[i] = 1000; // Dummy quantities - sharePrices[i] = 10000000000; // Dummy share prices - timestamps[i] = uint40(block.timestamp + i); // Dummy timestamps - } - - capTable.seedMultipleActivePositionsAndSecurityIds(stakeholderIds, securityIds, stockClassIds, quantities, sharePrices, timestamps); - - uint256 transactionCount = capTable.getTotalActiveSecuritiesCount(); - assertEq(transactionCount, 5); - } - - function testSeedingWithMismatchedArrayLengths() public { - bytes16[] memory stakeholderIds = new bytes16[](1); - bytes16[] memory securityIds = new bytes16[](2); // Mismatched length - bytes16[] memory stockClassIds = new bytes16[](1); - uint256[] memory quantities = new uint256[](1); - uint256[] memory sharePrices = new uint256[](1); - uint40[] memory timestamps = new uint40[](1); - - vm.expectRevert("Input arrays must have the same length"); - capTable.seedMultipleActivePositionsAndSecurityIds(stakeholderIds, securityIds, stockClassIds, quantities, sharePrices, timestamps); - } - - function testSeedingWithNonExistentStakeholdersOrStockClasses() public { - bytes16[] memory stakeholderIds = new bytes16[](1); - bytes16[] memory securityIds = new bytes16[](1); - bytes16[] memory stockClassIds = new bytes16[](1); - uint256[] memory quantities = new uint256[](1); - uint256[] memory sharePrices = new uint256[](1); - uint40[] memory timestamps = new uint40[](1); - - stakeholderIds[0] = 0x12345678901234567890123456789012; // Non-existent stakeholder - securityIds[0] = 0x12345678901234567890123456789012; - stockClassIds[0] = 0x12345678901234567890123456789012; // Non-existent stock class - quantities[0] = 1000; - sharePrices[0] = 10000000000; - timestamps[0] = uint40(block.timestamp); - - bytes memory expectedError = abi.encodeWithSignature("NoStakeholder(bytes16)", stakeholderIds[0]); - vm.expectRevert(expectedError); - - capTable.seedMultipleActivePositionsAndSecurityIds(stakeholderIds, securityIds, stockClassIds, quantities, sharePrices, timestamps); - } -} diff --git a/chain/test/Stakeholder.t.sol b/chain/test/Stakeholder.t.sol deleted file mode 100644 index 75bd9734..00000000 --- a/chain/test/Stakeholder.t.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "./CapTable.t.sol"; - -contract StakeholderTest is CapTableTest { - function testCreateStakeholder() public { - bytes16 stakeholderId = 0xf47ac10b58cc4372a5670e02b2c3d479; - capTable.createStakeholder(stakeholderId, "INDIVIDUAL", "ADVISOR"); - (bytes16 actualId, , ) = capTable.getStakeholderById(stakeholderId); - assertEq(actualId, stakeholderId); - } - - function testCreateNotAdminReverts() public { - bytes16 stakeholderId = 0xf47ac10b58cc4372a5670e02b2c3d479; - createPranksterAndExpectRevert(); - capTable.createStakeholder(stakeholderId, "INDIVIDUAL", "ADVISOR"); - } - - function testCreateDuplicateStakeholderReverts() public { - bytes16 stakeholderId = 0xf47ac10b58cc4372a5670e02b2c3d479; - capTable.createStakeholder(stakeholderId, "INDIVIDUAL", "ADVISOR"); - - // Since custom error passes ID, need to encode it to bytes - bytes memory expectedError = abi.encodeWithSignature("StakeholderAlreadyExists(bytes16)", stakeholderId); - - vm.expectRevert(expectedError); - capTable.createStakeholder(stakeholderId, "INDIVIDUAL", "ADVISOR"); - } - - function testGetTotalNumberOfStakeholders() public { - bytes16 firstStakeholderId = 0x123456789abcdef0123456789abcdef1; - bytes16 secondStakeholderId = 0xfedcba9876543210fedcba9876543210; - capTable.createStakeholder(firstStakeholderId, "INDIVIDUAL", "INVESTOR"); - capTable.createStakeholder(secondStakeholderId, "ENTITY", "BOARD_MEMBER"); - uint256 totalStakeholders = capTable.getTotalNumberOfStakeholders(); - assertEq(totalStakeholders, 2); - } -} diff --git a/chain/test/StakeholderNFT.t.sol b/chain/test/StakeholderNFT.t.sol index 6cb0f0e7..21b62244 100644 --- a/chain/test/StakeholderNFT.t.sol +++ b/chain/test/StakeholderNFT.t.sol @@ -6,7 +6,10 @@ import { StorageLib } from "@core/Storage.sol"; import { TxHelper, TxType } from "@libraries/TxHelper.sol"; import { ValidationLib } from "@libraries/ValidationLib.sol"; import { StakeholderPositions } from "@libraries/Structs.sol"; -import { StakeholderNFTFacet } from "@facets/StakeholderNFTFacet.sol"; +import { IssueStockParams } from "@libraries/Structs.sol"; +import { IStakeholderNFTFacet } from "@interfaces/IStakeholderNFTFacet.sol"; +import { AccessControl } from "@libraries/AccessControl.sol"; +import { IStockFacet } from "@interfaces/IStockFacet.sol"; contract DiamondStakeholderNFTTest is DiamondTestBase { bytes16 stakeholderId; @@ -14,22 +17,43 @@ contract DiamondStakeholderNFTTest is DiamondTestBase { function setUp() public override { super.setUp(); + + // Create stakeholder and set wallet (but don't link yet) stakeholderId = createStakeholder(); - stakeholderWallet = address(0xBEEF); + stakeholderWallet = address(0xF62849F9A0B5Bf2913b396098F7c7019b51A820a); + + // Grant necessary roles + vm.startPrank(contractOwner); + IAccessControlFacet(address(capTable)).grantRole(AccessControl.OPERATOR_ROLE, address(this)); + IAccessControlFacet(address(capTable)).grantRole(AccessControl.INVESTOR_ROLE, stakeholderWallet); + IAccessControlFacet(address(capTable)).grantRole(AccessControl.OPERATOR_ROLE, stakeholderWallet); + vm.stopPrank(); - // Issue some positions to the stakeholder - bytes16 stockClassId = createStockClass(); + // Create a stock class and issue some stock for the NFT metadata + bytes16 stockClassId = createStockClass(bytes16(uint128(1))); bytes16 stockSecurityId = 0xd3373e0a4dd940000000000000000001; - StockFacet(address(capTable)).issueStock(stockClassId, 1e18, 1000, stakeholderId, stockSecurityId); + bytes16 stockId = 0xd3373e0a4dd940000000000000000011; + IssueStockParams memory params = IssueStockParams({ + id: stockId, + stock_class_id: stockClassId, + share_price: 1e18, + quantity: 1000, + stakeholder_id: stakeholderId, + security_id: stockSecurityId, + custom_id: "custom_id", + stock_legend_ids_mapping: "stock_legend_ids_mapping", + security_law_exemptions_mapping: "security_law_exemptions_mapping" + }); + IStockFacet(address(capTable)).issueStock(params); } function testLinkStakeholderAddress() public { // Link the address - StakeholderFacet(address(capTable)).linkStakeholderAddress(stakeholderId, stakeholderWallet); + linkStakeholderAddress(stakeholderId, stakeholderWallet); - // Verify the link was created by trying to mint (which requires a valid link) + // Verify the link was created by trying to mint vm.prank(stakeholderWallet); - StakeholderNFTFacet(address(capTable)).mint(); + IStakeholderNFTFacet(address(capTable)).mint(); // If we get here without reverting, the link worked assertTrue(true, "Link successful - NFT minted"); @@ -37,61 +61,59 @@ contract DiamondStakeholderNFTTest is DiamondTestBase { function testMintNFT() public { // Link address first - StakeholderFacet(address(capTable)).linkStakeholderAddress(stakeholderId, stakeholderWallet); + linkStakeholderAddress(stakeholderId, stakeholderWallet); // Mint NFT vm.prank(stakeholderWallet); - StakeholderNFTFacet(address(capTable)).mint(); + IStakeholderNFTFacet(address(capTable)).mint(); } - function testFailMintWithoutLink() public { + function test_RevertMintWithoutLink() public { + // Try to mint without linking - should fail vm.prank(stakeholderWallet); - StakeholderNFTFacet(address(capTable)).mint(); + vm.expectRevert(abi.encodeWithSignature("NotStakeholder()")); + IStakeholderNFTFacet(address(capTable)).mint(); } - function testFailDoubleMint() public { + function test_RevertDoubleMint() public { // Link address first - StakeholderFacet(address(capTable)).linkStakeholderAddress(stakeholderId, stakeholderWallet); + linkStakeholderAddress(stakeholderId, stakeholderWallet); // First mint vm.prank(stakeholderWallet); - StakeholderNFTFacet(address(capTable)).mint(); + IStakeholderNFTFacet(address(capTable)).mint(); // Try to mint again - should fail vm.prank(stakeholderWallet); - StakeholderNFTFacet(address(capTable)).mint(); + vm.expectRevert(abi.encodeWithSignature("AlreadyMinted()")); + IStakeholderNFTFacet(address(capTable)).mint(); } function testTokenURI() public { - // Link address first - StakeholderFacet(address(capTable)).linkStakeholderAddress(stakeholderId, stakeholderWallet); + // Link address and mint NFT + linkStakeholderAddress(stakeholderId, stakeholderWallet); + + vm.startPrank(stakeholderWallet); // Mint NFT - vm.prank(stakeholderWallet); - StakeholderNFTFacet(address(capTable)).mint(); + IStakeholderNFTFacet(address(capTable)).mint(); + + vm.stopPrank(); // Get tokenId from stakeholderId uint256 tokenId = uint256(bytes32(stakeholderId)); - // Get URI - string memory uri = StakeholderNFTFacet(address(capTable)).tokenURI(tokenId); - console.log("Token URI:", uri); - - // Let's also log the positions directly - StakeholderPositions memory positions = StakeholderFacet(address(capTable)).getStakeholderPositions(stakeholderId); - - console.log("\nActive Positions:"); - console.log("Stock Positions:", positions.stocks.length); - if (positions.stocks.length > 0) { - for (uint i = 0; i < positions.stocks.length; i++) { - console.log(" Stock Position", i); - console.log(" Quantity:", positions.stocks[i].quantity); - console.log(" Share Price:", positions.stocks[i].share_price); - } - } - - console.log("Warrant Positions:", positions.warrants.length); - console.log("Convertible Positions:", positions.convertibles.length); - console.log("Equity Compensation Positions:", positions.equityCompensations.length); + // Get URI as stakeholderWallet (token owner) + string memory uri = IStakeholderNFTFacet(address(capTable)).tokenURI(tokenId); + + // Basic validation of URI format + assertTrue(bytes(uri).length > 0, "URI should not be empty"); + + // Also check positions exist + + StakeholderPositions memory positions = + IStakeholderFacet(address(capTable)).getStakeholderPositions(stakeholderId); + + assertTrue(positions.stocks.length > 0, "Should have stock positions"); } } diff --git a/chain/test/StakeholderPositions.t.sol b/chain/test/StakeholderPositions.t.sol index 0504683f..6ec289d3 100644 --- a/chain/test/StakeholderPositions.t.sol +++ b/chain/test/StakeholderPositions.t.sol @@ -2,7 +2,19 @@ pragma solidity ^0.8.0; import "./TestBase.sol"; -import { StockActivePosition, WarrantActivePosition, ConvertibleActivePosition, EquityCompensationActivePosition, StakeholderPositions } from "@libraries/Structs.sol"; +import { + StockActivePosition, + WarrantActivePosition, + ConvertibleActivePosition, + EquityCompensationActivePosition, + StakeholderPositions, + IssueStockParams, + IssueConvertibleParams, + IssueEquityCompensationParams +} from "@libraries/Structs.sol"; +import { IStockFacet } from "@interfaces/IStockFacet.sol"; +import { IConvertiblesFacet } from "@interfaces/IConvertiblesFacet.sol"; +import { IEquityCompensationFacet } from "@interfaces/IEquityCompensationFacet.sol"; contract DiamondStakeholderPositionsTest is DiamondTestBase { bytes16 stakeholderId; @@ -15,7 +27,7 @@ contract DiamondStakeholderPositionsTest is DiamondTestBase { function setUp() public override { super.setUp(); stakeholderId = createStakeholder(); - stockClassId = createStockClass(); + stockClassId = createStockClass(bytes16(uint128(12))); bytes16[] memory stockClassIds = new bytes16[](1); stockClassIds[0] = stockClassId; @@ -23,19 +35,62 @@ contract DiamondStakeholderPositionsTest is DiamondTestBase { // Issue stock stockSecurityId = 0xd3373e0a4dd940000000000000000001; - StockFacet(address(capTable)).issueStock(stockClassId, 1e18, 1000, stakeholderId, stockSecurityId); + bytes16 stockId = 0xd3373e0a4dd940000000000000000011; + IssueStockParams memory params = IssueStockParams({ + id: stockId, + stock_class_id: stockClassId, + share_price: 1e18, + quantity: 1000, + stakeholder_id: stakeholderId, + security_id: stockSecurityId, + custom_id: "STOCK_POS_001", + stock_legend_ids_mapping: "LEGEND_1", + security_law_exemptions_mapping: "REG_D" + }); + IStockFacet(address(capTable)).issueStock(params); // Issue convertible convertibleSecurityId = 0xd3373e0a4dd940000000000000000002; - ConvertiblesFacet(address(capTable)).issueConvertible(stakeholderId, 1000000, convertibleSecurityId); + bytes16 convertibleId = 0xd3373e0a4dd940000000000000000012; + + IssueConvertibleParams memory convertibleParams = IssueConvertibleParams({ + id: convertibleId, + stakeholder_id: stakeholderId, + investment_amount: 1_000_000, + security_id: convertibleSecurityId, + convertible_type: "SAFE", + seniority: 1, + custom_id: "CONV_POS_001", + security_law_exemptions_mapping: "REG_D", + conversion_triggers_mapping: "CONVERSION_ON_NEXT_EQUITY" + }); + IConvertiblesFacet(address(capTable)).issueConvertible(convertibleParams); // Issue equity compensation equityCompSecurityId = 0xd3373e0a4dd940000000000000000003; - EquityCompensationFacet(address(capTable)).issueEquityCompensation(stakeholderId, stockClassId, stockPlanId, 1000, equityCompSecurityId); + bytes16 equityCompensationId = 0xd3373e0a4dd940000000000000000013; + + IssueEquityCompensationParams memory equityParams = IssueEquityCompensationParams({ + id: equityCompensationId, + stakeholder_id: stakeholderId, + stock_class_id: stockClassId, + stock_plan_id: stockPlanId, + quantity: 1000, + security_id: equityCompSecurityId, + compensation_type: "ISO", + exercise_price: 1e18, + base_price: 1e18, + expiration_date: "2025-12-31", + custom_id: "EQCOMP_POS_001", + termination_exercise_windows_mapping: "90_DAYS", + security_law_exemptions_mapping: "REG_D" + }); + IEquityCompensationFacet(address(capTable)).issueEquityCompensation(equityParams); } function testGetStakeholderPositions() public { - StakeholderPositions memory positions = StakeholderFacet(address(capTable)).getStakeholderPositions(stakeholderId); + StakeholderPositions memory positions = + IStakeholderFacet(address(capTable)).getStakeholderPositions(stakeholderId); // Verify stock position assertEq(positions.stocks.length, 1); @@ -47,7 +102,7 @@ contract DiamondStakeholderPositionsTest is DiamondTestBase { // Verify convertible position assertEq(positions.convertibles.length, 1); assertEq(positions.convertibles[0].stakeholder_id, stakeholderId); - assertEq(positions.convertibles[0].investment_amount, 1000000); + assertEq(positions.convertibles[0].investment_amount, 1_000_000); // Verify equity compensation position assertEq(positions.equityCompensations.length, 1); diff --git a/chain/test/StockAcceptance.t.sol b/chain/test/StockAcceptance.t.sol deleted file mode 100644 index bf58bb5a..00000000 --- a/chain/test/StockAcceptance.t.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "forge-std/console.sol"; - -import "./CapTable.t.sol"; -import { InitialShares, IssuerInitialShares, StockClassInitialShares, Issuer, StockClass, StockIssuanceParams, ShareNumbersIssued, StockParams } from "../src/lib/Structs.sol"; - -contract StockAcceptanceTest is CapTableTest { - function testStockAcceptance() public { - // Create stock class and stakeholder - (bytes16 stockClassId, bytes16 stakeholderId) = createStockClassAndStakeholder(1000000); - - // using helper found in CapTable.t.sol - issueStock(stockClassId, stakeholderId, 1000); - - // Accept stock - uint256 lastTransactionIndex = capTable.getTransactionsCount() - 1; - bytes memory lastTransaction = capTable.transactions(lastTransactionIndex); - StockIssuance memory lastIssuance = abi.decode(lastTransaction, (StockIssuance)); - string[] memory comments = new string[](1); - comments[0] = "Acceptance of stock"; - capTable.acceptStock(stakeholderId, stockClassId, lastIssuance.security_id, comments); - - // Assert last transaction is of type acceptance - lastTransactionIndex = capTable.getTransactionsCount() - 1; - lastTransaction = capTable.transactions(lastTransactionIndex); - StockAcceptance memory lastAcceptance = abi.decode(lastTransaction, (StockAcceptance)); - assertEq(lastAcceptance.object_type, "TX_STOCK_ACCEPTANCE"); - assertEq(lastAcceptance.security_id, lastIssuance.security_id); - assertEq(lastAcceptance.comments[0], comments[0]); - } -} diff --git a/chain/test/StockCancellation.t.sol b/chain/test/StockCancellation.t.sol deleted file mode 100644 index 3178ff76..00000000 --- a/chain/test/StockCancellation.t.sol +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "forge-std/console.sol"; - -import "./CapTable.t.sol"; -import { InitialShares, IssuerInitialShares, StockClassInitialShares, Issuer, StockClass, StockIssuanceParams, ShareNumbersIssued, StockIssuance, StockTransfer, StockParams } from "../src/lib/Structs.sol"; - -contract StockCancellationTest is CapTableTest { - function testFullStockCancellation() public { - (bytes16 stockClassId, bytes16 stakeholderId) = createStockClassAndStakeholder(1000000); - - issueStock(stockClassId, stakeholderId, 1000); - - uint256 lastTransactionIndex = capTable.getTransactionsCount() - 1; - bytes memory lastTransaction = capTable.transactions(lastTransactionIndex); - StockIssuance memory lastIssuance = abi.decode(lastTransaction, (StockIssuance)); - - // Cancel stock - capTable.cancelStock( - StockParams({ - stakeholder_id: stakeholderId, - stock_class_id: stockClassId, - security_id: lastIssuance.security_id, - reason_text: "The issued party failed to meet their obligations", - comments: new string[](0) - }), - lastIssuance.params.quantity - ); - - // Assert last transaction is of type cancellation - lastTransactionIndex = capTable.getTransactionsCount() - 1; - lastTransaction = capTable.transactions(lastTransactionIndex); - StockCancellation memory lastCancellation = abi.decode(lastTransaction, (StockCancellation)); - assertEq(lastCancellation.object_type, "TX_STOCK_CANCELLATION"); - assertEq(lastCancellation.quantity, lastIssuance.params.quantity); - } - - function testNoActivePositionCancellationRevert() public { - // Create stock class and stakeholder - (bytes16 stockClassId, bytes16 stakeholderId) = createStockClassAndStakeholder(1000000); - - issueStock(stockClassId, stakeholderId, 1000); - - bytes16 nonExistentSecId = bytes16("0xd3373e0a4dd86"); - - // Expecting the ActivePositionNotFound revert - bytes memory expectedError = abi.encodeWithSignature("ActivePositionNotFound(bytes16,bytes16)", stakeholderId, nonExistentSecId); - vm.expectRevert(expectedError); - - uint256 partialCancellationQuantity = 500; - - // cancel a stock with a security_id that doesn't exist, therefore no ActivePosition present. - capTable.cancelStock( - StockParams({ - stakeholder_id: stakeholderId, - stock_class_id: stockClassId, - security_id: nonExistentSecId, - reason_text: "Partial cancellation", - comments: new string[](0) - }), - partialCancellationQuantity - ); - } - - function testCancelMoreThanAvailableRevert() public { - // Create stock class and stakeholder - (bytes16 stockClassId, bytes16 stakeholderId) = createStockClassAndStakeholder(1000000); - - issueStock(stockClassId, stakeholderId, 1000); // issues for 1000 shares. - - uint256 lastTransactionIndex = capTable.getTransactionsCount() - 1; - bytes memory lastTransaction = capTable.transactions(lastTransactionIndex); - StockIssuance memory lastIssuance = abi.decode(lastTransaction, (StockIssuance)); - - uint256 quantityToCancel = 1200; // Cancel 200 more than available - - // if quantity is 0, there's no ActivePosition that exists. - bytes memory expectedError = abi.encodeWithSignature("InsufficientShares(uint256,uint256)", 1000, quantityToCancel); - vm.expectRevert(expectedError); - - capTable.cancelStock( - StockParams({ - stakeholder_id: stakeholderId, - stock_class_id: stockClassId, - security_id: lastIssuance.security_id, - reason_text: "Partial cancellation", - comments: new string[](0) - }), - quantityToCancel - ); - } - - function testPartialStockCancellation() public { - // Create stock class and stakeholder - (bytes16 stockClassId, bytes16 stakeholderId) = createStockClassAndStakeholder(1000000); - - issueStock(stockClassId, stakeholderId, 1000); - - // Cancel part of the stock - uint256 issuanceTx = capTable.getTransactionsCount() - 1; - bytes memory issuance = capTable.transactions(issuanceTx); - StockIssuance memory firstIssuance = abi.decode(issuance, (StockIssuance)); - uint256 partialCancellationQuantity = 500; // Cancel only part of the stock - capTable.cancelStock( - StockParams({ - stakeholder_id: stakeholderId, - stock_class_id: stockClassId, - security_id: firstIssuance.security_id, - reason_text: "Partial cancellation", - comments: new string[](0) - }), - partialCancellationQuantity - ); - - // Assert last transaction is of type issuance with the remaining amount - uint256 secondLastTransactionIndex = capTable.getTransactionsCount() - 2; - bytes memory secondLastTransaction = capTable.transactions(secondLastTransactionIndex); - StockIssuance memory secondIssuance = abi.decode(secondLastTransaction, (StockIssuance)); - assertEq(secondIssuance.object_type, "TX_STOCK_ISSUANCE"); - assertEq(secondIssuance.params.quantity, firstIssuance.params.quantity - partialCancellationQuantity); - - // Assert last transaction is of type cancellation - uint256 lastTransactionIndex = capTable.getTransactionsCount() - 1; - bytes memory lastTransaction = capTable.transactions(lastTransactionIndex); - StockCancellation memory cancellation = abi.decode(lastTransaction, (StockCancellation)); - assertEq(cancellation.object_type, "TX_STOCK_CANCELLATION"); - assertEq(cancellation.quantity, partialCancellationQuantity); - - // Assert issuer and stock class shares_issued - (, , uint256 issuerSharesIssued, ) = capTable.issuer(); - assertEq(issuerSharesIssued, firstIssuance.params.quantity - partialCancellationQuantity); - - (, , , uint256 stockClassSharesIssued, ) = capTable.getStockClassById(stockClassId); - assertEq(stockClassSharesIssued, firstIssuance.params.quantity - partialCancellationQuantity); - } -} diff --git a/chain/test/StockClass.t.sol b/chain/test/StockClass.t.sol deleted file mode 100644 index 3e6bbdec..00000000 --- a/chain/test/StockClass.t.sol +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "./CapTable.t.sol"; -import { InitialShares, IssuerInitialShares, StockClassInitialShares, Issuer, StockClass } from "../src/lib/Structs.sol"; - -contract StockClassTest is CapTableTest { - function createInitialDummyStockClassData() public pure returns (bytes16, string memory, uint256, uint256, uint256) { - bytes16 expectedId = 0xd3373e0a4dd9430f8a563281f2454545; - string memory expectedClassType = "Common"; - uint256 expectedPricePerShare = 10000000000; // $1.00 with 10 decimals - uint256 expectedInitialSharesAuthorized = 100000000000000000; // 10,000,000 - uint256 expectedSharesIssued = 0; - return (expectedId, expectedClassType, expectedPricePerShare, expectedInitialSharesAuthorized, expectedSharesIssued); - } - - function testCreateStockClass() public { - ( - bytes16 expectedId, - string memory expectedClassType, - uint256 expectedPricePerShare, - uint256 expectedInitialSharesAuthorized, - uint256 expectedSharesIssued - ) = createInitialDummyStockClassData(); - capTable.createStockClass(expectedId, expectedClassType, expectedPricePerShare, expectedInitialSharesAuthorized); - ( - bytes16 actualId, - string memory actualClassType, - uint256 actualPricePerShare, - uint256 actualSharesIssued, - uint256 actualInitialSharesAuthorized - ) = capTable.getStockClassById(expectedId); - assertEq(actualId, expectedId); - assertEq(actualClassType, expectedClassType); - assertEq(actualPricePerShare, expectedPricePerShare); - assertEq(actualInitialSharesAuthorized, expectedInitialSharesAuthorized); - assertEq(actualSharesIssued, expectedSharesIssued); - } - - function testCreateNotAdminReverts() public { - createPranksterAndExpectRevert(); - - ( - bytes16 expectedId, - string memory expectedClassType, - uint256 expectedPricePerShare, - uint256 expectedInitialSharesAuthorized, - - ) = createInitialDummyStockClassData(); - capTable.createStockClass(expectedId, expectedClassType, expectedPricePerShare, expectedInitialSharesAuthorized); - } - - function testCreateDuplicateStockClassReverts() public { - ( - bytes16 expectedId, - string memory expectedClassType, - uint256 expectedPricePerShare, - uint256 expectedInitialSharesAuthorized, - - ) = createInitialDummyStockClassData(); - capTable.createStockClass(expectedId, expectedClassType, expectedPricePerShare, expectedInitialSharesAuthorized); - - bytes memory expectedError = abi.encodeWithSignature("StockClassAlreadyExists(bytes16)", expectedId); - vm.expectRevert(expectedError); - - capTable.createStockClass(expectedId, expectedClassType, expectedPricePerShare, expectedInitialSharesAuthorized); - } - - function testGetTotalNumberOfStockClasses() public { - bytes16[5] memory stockClassIds = [ - bytes16(0x11111111111111111111111111111111), - bytes16(0x22222222222222222222222222222222), - bytes16(0x33333333333333333333333333333333), - bytes16(0x44444444444444444444444444444444), - bytes16(0x55555555555555555555555555555555) - ]; - for (uint256 i = 0; i < stockClassIds.length; i++) { - capTable.createStockClass(stockClassIds[i], "Common", 10000000000, issuerInitialSharesAuthorized - 1); - } - uint256 totalStockClasses = capTable.getTotalNumberOfStockClasses(); - assertEq(totalStockClasses, 5); - } -} diff --git a/chain/test/StockIssuance.t.sol b/chain/test/StockIssuance.t.sol index 7e150cee..50fd1697 100644 --- a/chain/test/StockIssuance.t.sol +++ b/chain/test/StockIssuance.t.sol @@ -4,6 +4,8 @@ pragma solidity ^0.8.0; import "./TestBase.sol"; import { StorageLib } from "@core/Storage.sol"; import { TxHelper, TxType } from "@libraries/TxHelper.sol"; +import { IssueStockParams } from "@libraries/Structs.sol"; +import { IStockFacet } from "@interfaces/IStockFacet.sol"; contract DiamondStockIssuanceTest is DiamondTestBase { function createStockClassAndStakeholder(uint256 sharesAuthorized) public returns (bytes16, bytes16) { @@ -12,55 +14,123 @@ contract DiamondStockIssuanceTest is DiamondTestBase { vm.expectEmit(true, false, false, false, address(capTable)); emit StakeholderCreated(stakeholderId); - StakeholderFacet(payable(address(capTable))).createStakeholder(stakeholderId); + IStakeholderFacet(address(capTable)).createStakeholder(stakeholderId); vm.expectEmit(true, true, false, false, address(capTable)); emit StockClassCreated(stockClassId, "COMMON", 100, sharesAuthorized); - StockClassFacet(payable(address(capTable))).createStockClass(stockClassId, "COMMON", 100, sharesAuthorized); + IStockClassFacet(address(capTable)).createStockClass(stockClassId, "COMMON", 100, sharesAuthorized); return (stockClassId, stakeholderId); } function testIssueStock() public { - (bytes16 stockClassId, bytes16 stakeholderId) = createStockClassAndStakeholder(100000); - + (bytes16 stockClassId, bytes16 stakeholderId) = createStockClassAndStakeholder(100_000); bytes16 securityId = 0xd3373e0a4dd940000000000000000001; - uint256 sharePrice = 10000000000; + bytes16 id = 0xd3373e0a4dd940000000000000000010; + uint256 sharePrice = 10_000_000_000; uint256 quantity = 1000; + IssueStockParams memory params = IssueStockParams({ + id: id, + stock_class_id: stockClassId, + share_price: sharePrice, + quantity: quantity, + stakeholder_id: stakeholderId, + security_id: securityId, + custom_id: "STOCK_001", + stock_legend_ids_mapping: "LEGEND_1", + security_law_exemptions_mapping: "REG_D" + }); + vm.expectEmit(true, true, false, true, address(capTable)); - emit TxHelper.TxCreated(TxType.STOCK_ISSUANCE, abi.encode(stockClassId, sharePrice, quantity, stakeholderId, securityId)); + emit TxHelper.TxCreated(TxType.STOCK_ISSUANCE, abi.encode(params)); - StockFacet(address(capTable)).issueStock(stockClassId, sharePrice, quantity, stakeholderId, securityId); + IStockFacet(address(capTable)).issueStock(params); } - function testFailInvalidStakeholder() public { + function test_RevertInvalidStakeholder() public { bytes16 invalidStakeholderId = 0xd3373e0a4dd940000000000000000099; bytes16 stockClassId = 0xd3373e0a4dd940000000000000000000; bytes16 securityId = 0xd3373e0a4dd940000000000000000001; - - StockFacet(address(capTable)).issueStock(stockClassId, 10000000000, 1000, invalidStakeholderId, securityId); + bytes16 id = 0xd3373e0a4dd940000000000000000002; + + IssueStockParams memory params = IssueStockParams({ + id: id, + stock_class_id: stockClassId, + share_price: 10_000_000_000, + quantity: 1000, + stakeholder_id: invalidStakeholderId, + security_id: securityId, + custom_id: "STOCK_002", + stock_legend_ids_mapping: "LEGEND_1", + security_law_exemptions_mapping: "REG_D" + }); + + vm.expectRevert(abi.encodeWithSignature("NoStakeholder(bytes16)", invalidStakeholderId)); + IStockFacet(address(capTable)).issueStock(params); } - function testFailInvalidStockClass() public { - (, bytes16 stakeholderId) = createStockClassAndStakeholder(100000); + function test_RevertInvalidStockClass() public { + (, bytes16 stakeholderId) = createStockClassAndStakeholder(100_000); bytes16 invalidStockClassId = 0xd3373e0a4dd940000000000000000099; bytes16 securityId = 0xd3373e0a4dd940000000000000000001; - - StockFacet(address(capTable)).issueStock(invalidStockClassId, 10000000000, 1000, stakeholderId, securityId); + bytes16 id = 0xd3373e0a4dd940000000000000000002; + + IssueStockParams memory params = IssueStockParams({ + id: id, + stock_class_id: invalidStockClassId, + share_price: 10_000_000_000, + quantity: 1000, + stakeholder_id: stakeholderId, + security_id: securityId, + custom_id: "STOCK_003", + stock_legend_ids_mapping: "LEGEND_1", + security_law_exemptions_mapping: "REG_D" + }); + + vm.expectRevert(abi.encodeWithSignature("InvalidStockClass(bytes16)", invalidStockClassId)); + IStockFacet(address(capTable)).issueStock(params); } - function testFailInsufficientIssuerShares() public { + function test_RevertInsufficientIssuerShares() public { (bytes16 stockClassId, bytes16 stakeholderId) = createStockClassAndStakeholder(100); bytes16 securityId = 0xd3373e0a4dd940000000000000000001; - - StockFacet(address(capTable)).issueStock(stockClassId, 10000000000, 1000, stakeholderId, securityId); + bytes16 id = 0xd3373e0a4dd940000000000000000002; + + IssueStockParams memory params = IssueStockParams({ + id: id, + stock_class_id: stockClassId, + share_price: 10_000_000_000, + quantity: 1000, + stakeholder_id: stakeholderId, + security_id: securityId, + custom_id: "STOCK_004", + stock_legend_ids_mapping: "LEGEND_1", + security_law_exemptions_mapping: "REG_D" + }); + + vm.expectRevert("StockClass: Insufficient shares authorized"); + IStockFacet(address(capTable)).issueStock(params); } - function testFailInsufficientStockClassShares() public { + function test_RevertInsufficientStockClassShares() public { (bytes16 stockClassId, bytes16 stakeholderId) = createStockClassAndStakeholder(100); bytes16 securityId = 0xd3373e0a4dd940000000000000000001; - - StockFacet(address(capTable)).issueStock(stockClassId, 10000000000, 101, stakeholderId, securityId); + bytes16 id = 0xd3373e0a4dd940000000000000000002; + + IssueStockParams memory params = IssueStockParams({ + id: id, + stock_class_id: stockClassId, + share_price: 10_000_000_000, + quantity: 101, + stakeholder_id: stakeholderId, + security_id: securityId, + custom_id: "STOCK_005", + stock_legend_ids_mapping: "LEGEND_1", + security_law_exemptions_mapping: "REG_D" + }); + + vm.expectRevert("StockClass: Insufficient shares authorized"); + IStockFacet(address(capTable)).issueStock(params); } } diff --git a/chain/test/StockReissuance.t.sol b/chain/test/StockReissuance.t.sol deleted file mode 100644 index eff44d34..00000000 --- a/chain/test/StockReissuance.t.sol +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "forge-std/console.sol"; - -import "./CapTable.t.sol"; -import { InitialShares, IssuerInitialShares, StockClassInitialShares, Issuer, StockClass, StockIssuanceParams, ShareNumbersIssued, StockIssuance, StockParams, StockReissuance } from "../src/lib/Structs.sol"; - -contract StockReissuanceTest is CapTableTest { - function testStockReissuance() public { - (bytes16 stockClassId, bytes16 stakeholderId) = createStockClassAndStakeholder(1000000); - - uint256 issuanceQuantity = 1000; - - // First issuance to reissue as security_id - issueStock(stockClassId, stakeholderId, issuanceQuantity); - // Second issuance as resulting_security_id - issueStock(stockClassId, stakeholderId, issuanceQuantity); - - // last transaction = resulting_security_id issuance - bytes memory newIssuance = capTable.transactions(capTable.getTransactionsCount() - 1); - StockIssuance memory issuanceReissued = abi.decode(newIssuance, (StockIssuance)); - - // second to last transaction to reissue = security_id - bytes memory issuanceToReissue = capTable.transactions(capTable.getTransactionsCount() - 2); - StockIssuance memory issuanceToDelete = abi.decode(issuanceToReissue, (StockIssuance)); - - (, , uint256 issuerSharesIssuedBefore, ) = capTable.issuer(); - - uint256 totalSharesIssued = issuanceQuantity * 2; - - // ensure total issued matches. - assertEq(issuerSharesIssuedBefore, totalSharesIssued); - - // Perform reissuance - bytes16[] memory resulting_security_ids = new bytes16[](1); - resulting_security_ids[0] = issuanceReissued.security_id; - - capTable.reissueStock( - StockParams({ - stakeholder_id: stakeholderId, - stock_class_id: stockClassId, - security_id: issuanceToDelete.security_id, - reason_text: "Reissuance for testing", - comments: new string[](0) - }), - resulting_security_ids - ); - - // Assert last transaction is of type reissuance - uint256 transactionsCount = capTable.getTransactionsCount(); - bytes memory lastTransaction = capTable.transactions(transactionsCount - 1); - StockReissuance memory reissuance = abi.decode(lastTransaction, (StockReissuance)); - assertEq(reissuance.object_type, "TX_STOCK_REISSUANCE"); - - // Assert that the issuer shares issued is 1000 after reissuance, since it was 2000 initially - (, , uint256 issuerSharesIssuedAfter, ) = capTable.issuer(); - assertEq(issuerSharesIssuedAfter, issuanceQuantity); - } - - function testStockReissuanceNoResultingSecurityIdReverts() public { - (bytes16 stockClassId, bytes16 stakeholderId) = createStockClassAndStakeholder(1000000); - - uint256 issuanceQuantity = 1000; - - // First issuance to reissue as security_id - issueStock(stockClassId, stakeholderId, issuanceQuantity); - - bytes memory newIssuance = capTable.transactions(capTable.getTransactionsCount() - 1); - StockIssuance memory issuance = abi.decode(newIssuance, (StockIssuance)); - - // empty array should fail - bytes16[] memory resulting_security_ids = new bytes16[](0); - - bytes memory expectedError = abi.encodeWithSignature("NoIssuanceFound()"); - vm.expectRevert(expectedError); - - capTable.reissueStock( - StockParams({ - stakeholder_id: stakeholderId, - stock_class_id: stockClassId, - security_id: issuance.security_id, - reason_text: "Reissuance for testing", - comments: new string[](0) - }), - resulting_security_ids - ); - } - - function testStockReissuanceNoSecurityIdReverts() public { - (bytes16 stockClassId, bytes16 stakeholderId) = createStockClassAndStakeholder(1000000); - - uint256 issuanceQuantity = 1000; - - // First issuance to reissue as security_id - issueStock(stockClassId, stakeholderId, issuanceQuantity); - - bytes memory newIssuance = capTable.transactions(capTable.getTransactionsCount() - 1); - StockIssuance memory issuance = abi.decode(newIssuance, (StockIssuance)); - - // empty array should fail - bytes16[] memory resulting_security_ids = new bytes16[](1); - resulting_security_ids[0] = issuance.security_id; - - bytes16 nonExistingSecId = 0x00000000000000000000000000000000; - - // Expecting the ActivePositionNotFound revert - bytes memory expectedError = abi.encodeWithSignature("ActivePositionNotFound(bytes16,bytes16)", stakeholderId, nonExistingSecId); - vm.expectRevert(expectedError); - - capTable.reissueStock( - StockParams({ - stakeholder_id: stakeholderId, - stock_class_id: stockClassId, - security_id: nonExistingSecId, - reason_text: "Reissuance for testing", - comments: new string[](0) - }), - resulting_security_ids - ); - - capTable.getTransactionsCount(); - } -} diff --git a/chain/test/StockRepurchase.t.sol b/chain/test/StockRepurchase.t.sol deleted file mode 100644 index 0384304c..00000000 --- a/chain/test/StockRepurchase.t.sol +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "forge-std/console.sol"; - -import "./CapTable.t.sol"; -import { StockIssuanceParams, ShareNumbersIssued, StockIssuance, StockParams, StockRepurchase } from "../src/lib/Structs.sol"; - -contract StockRepurchaseTest is CapTableTest { - function testPartialStockRepurchase() public { - // Create stock class and stakeholder - (bytes16 stockClassId, bytes16 stakeholderId) = createStockClassAndStakeholder(1000000); - - uint256 issuanceQuantity = 1000; - // Issue stock - issueStock(stockClassId, stakeholderId, issuanceQuantity); - - // repurchase last issuance - bytes memory issuanceTx = capTable.transactions(capTable.getTransactionsCount() - 1); - StockIssuance memory issuance = abi.decode(issuanceTx, (StockIssuance)); - - // Repurchase part of the stock - uint256 partialRepurchaseQuantity = 300; - uint256 repurchasePrice = 2000; - - capTable.repurchaseStock( - StockParams({ - stakeholder_id: stakeholderId, - stock_class_id: stockClassId, - security_id: issuance.security_id, - reason_text: "Partial repurchase", - comments: new string[](0) - }), - partialRepurchaseQuantity, - repurchasePrice - ); - - uint256 transactionsCount = capTable.getTransactionsCount(); - bytes memory remainingIssuanceTx = capTable.transactions(transactionsCount - 2); - StockIssuance memory remainingIssuance = abi.decode(remainingIssuanceTx, (StockIssuance)); - - assertEq(remainingIssuance.params.quantity, issuanceQuantity - partialRepurchaseQuantity); - // Assert security ID of initial issuance is not the same as remaining balance issuance - assertNotEq(issuance.security_id, remainingIssuance.security_id); - - // Assert last transaction is of type repurchase - bytes memory lastTransaction = capTable.transactions(transactionsCount - 1); - StockRepurchase memory repurchase = abi.decode(lastTransaction, (StockRepurchase)); - - // verify that the original issuance and new balance issuance don't collide in security_ids - assertNotEq(repurchase.security_id, repurchase.balance_security_id); - - assertEq(repurchase.object_type, "TX_STOCK_REPURCHASE"); - assertEq(repurchase.price, repurchasePrice); - assertEq(repurchase.quantity, partialRepurchaseQuantity); - - // Assert issuer and stock class shares_issued - (, , uint256 issuerSharesIssued, ) = capTable.issuer(); - assertEq(issuerSharesIssued, issuanceQuantity - partialRepurchaseQuantity); - - (, , , uint256 stockClassSharesIssued, ) = capTable.getStockClassById(stockClassId); - assertEq(stockClassSharesIssued, issuanceQuantity - partialRepurchaseQuantity); - } - - function testFullStockRepurchase() public { - (bytes16 stockClassId, bytes16 stakeholderId) = createStockClassAndStakeholder(1000000); - - uint256 fullRepurchaseQuantity = 1000; - - issueStock(stockClassId, stakeholderId, fullRepurchaseQuantity); - - // Assert last transaction is of type issuance with the remaining amount - uint256 issuanceTxIdx = capTable.getTransactionsCount() - 1; - bytes memory issuanceTx = capTable.transactions(issuanceTxIdx); - StockIssuance memory issuance = abi.decode(issuanceTx, (StockIssuance)); - - // Repurchase all of the stock - uint256 repurchasePrice = 99; - - capTable.repurchaseStock( - StockParams({ - stakeholder_id: stakeholderId, - stock_class_id: stockClassId, - security_id: issuance.security_id, - reason_text: "Full repurchase", - comments: new string[](0) - }), - fullRepurchaseQuantity, - repurchasePrice - ); - - uint256 transactionsCount = capTable.getTransactionsCount(); - uint256 remainingIssuaneTxIndex = transactionsCount - 2; - bytes memory remainingIssuanceTx = capTable.transactions(remainingIssuaneTxIndex); - StockIssuance memory repurchaseIssuance = abi.decode(remainingIssuanceTx, (StockIssuance)); - - assertEq(repurchaseIssuance.params.quantity, fullRepurchaseQuantity); - - uint256 lastTransactionIndex = transactionsCount - 1; - bytes memory lastTransaction = capTable.transactions(lastTransactionIndex); - StockRepurchase memory repurchase = abi.decode(lastTransaction, (StockRepurchase)); - - assertEq(repurchase.object_type, "TX_STOCK_REPURCHASE"); - assertEq(repurchase.price, repurchasePrice); - - // Assert issuer and stock class shares_issued - (, , uint256 issuerSharesIssued, ) = capTable.issuer(); - assertEq(issuerSharesIssued, issuance.params.quantity - repurchaseIssuance.params.quantity); - } -} diff --git a/chain/test/StockRetraction.t.sol b/chain/test/StockRetraction.t.sol deleted file mode 100644 index 76e1394e..00000000 --- a/chain/test/StockRetraction.t.sol +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "forge-std/console.sol"; - -import "./CapTable.t.sol"; -import { InitialShares, IssuerInitialShares, StockClassInitialShares, Issuer, StockClass, StockIssuanceParams, ShareNumbersIssued, StockIssuance, StockTransfer, StockParams } from "../src/lib/Structs.sol"; - -contract StockRetractionTest is CapTableTest { - function testStockRetraction() public { - // Create stock class and stakeholder - (bytes16 stockClassId, bytes16 stakeholderId) = createStockClassAndStakeholder(1000000); - - uint256 issuanceQuantity = 1000; - // Issue stock - issueStock(stockClassId, stakeholderId, issuanceQuantity); - - // repurchase last issuance - bytes memory issuanceTx = capTable.transactions(capTable.getTransactionsCount() - 1); - StockIssuance memory issuance = abi.decode(issuanceTx, (StockIssuance)); - - // Retract stock - StockParams memory retractionParams = StockParams({ - stakeholder_id: stakeholderId, - stock_class_id: stockClassId, - security_id: issuance.security_id, - comments: new string[](0), - reason_text: "Retraction for test" - }); - capTable.retractStockIssuance(retractionParams); - - // Assert last transaction is of type retraction - uint256 lastTransactionIndex = capTable.getTransactionsCount() - 1; - bytes memory lastTransaction = capTable.transactions(lastTransactionIndex); - StockRetraction memory lastRetraction = abi.decode(lastTransaction, (StockRetraction)); - - assertEq(lastRetraction.object_type, "TX_STOCK_RETRACTION"); - assertEq(lastRetraction.reason_text, retractionParams.reason_text); - - // Assert issuer and stock class shares_issued should both be zero - (, , uint256 issuerSharesIssued, ) = capTable.issuer(); - assertEq(issuerSharesIssued, 0); - - (, , , uint256 stockClassSharesIssued, ) = capTable.getStockClassById(stockClassId); - assertEq(stockClassSharesIssued, 0); - } -} diff --git a/chain/test/StockTransfer.t.sol b/chain/test/StockTransfer.t.sol index 3216ed28..8c152a21 100644 --- a/chain/test/StockTransfer.t.sol +++ b/chain/test/StockTransfer.t.sol @@ -1,71 +1,332 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity ^0.8.0; -import "forge-std/console.sol"; +import "./TestBase.sol"; +import { StorageLib } from "@core/Storage.sol"; +import { TxHelper, TxType } from "@libraries/TxHelper.sol"; +import { IssueStockParams, StockActivePosition } from "@libraries/Structs.sol"; +import { IStockFacet } from "@interfaces/IStockFacet.sol"; +import { IIssuerFacet } from "@interfaces/IIssuerFacet.sol"; +import { IStockClassFacet } from "@interfaces/IStockClassFacet.sol"; +import { ValidationLib } from "@libraries/ValidationLib.sol"; +import { StockFacet } from "@facets/StockFacet.sol"; -import "./CapTable.t.sol"; -import { InitialShares, IssuerInitialShares, StockClassInitialShares, Issuer, StockClass, StockIssuanceParams, ShareNumbersIssued, StockIssuance, StockTransfer, StockParams } from "../src/lib/Structs.sol"; +contract DiamondStockTransferTest is DiamondTestBase { + bytes16 public transferorId; + bytes16 public transfereeId; + bytes16 public stockClassId; + bytes16 public securityId; + uint256 public constant INITIAL_SHARES = 1000; + uint256 public constant SHARE_PRICE = 100; -contract StockTransferTest is CapTableTest { - function createTransferSetup() private returns (bytes16, bytes16, bytes16, uint256) { - // Create stakeholders - bytes16 transferorStakeholderId = 0xd3373e0a4dd940000000000000000006; - bytes16 transfereeStakeholderId = 0xd3373e0a4dd940000000000000000007; - capTable.createStakeholder(transferorStakeholderId, "INDIVIDUAL", "EMPLOYEE"); - capTable.createStakeholder(transfereeStakeholderId, "INDIVIDUAL", "EMPLOYEE"); + function setUp() public override { + super.setUp(); - // Create stock class - bytes16 stockClassId = 0xd3373e0a4dd940000000000000000008; - capTable.createStockClass(stockClassId, "Common", 100, 1000000); + // Create stock class and stakeholders + stockClassId = createStockClass(bytes16(uint128(9))); + transferorId = createStakeholder(); + transfereeId = bytes16(uint128(transferorId) + 1); // Create a different ID + IStakeholderFacet(address(capTable)).createStakeholder(transfereeId); - uint256 firstIssuanceQty = 3000; - uint256 secondIssuanceQty = 2000; + // Issue initial shares to transferor + securityId = bytes16(uint128(transferorId) + 2); // Create a different ID + IssueStockParams memory params = IssueStockParams({ + id: bytes16(uint128(transferorId) + 3), + stock_class_id: stockClassId, + share_price: SHARE_PRICE, + quantity: INITIAL_SHARES, + stakeholder_id: transferorId, + security_id: securityId, + custom_id: "STOCK_001", + stock_legend_ids_mapping: "LEGEND_1", + security_law_exemptions_mapping: "REG_D" + }); - // Issue twice to the same stakeholder - issueStock(stockClassId, transferorStakeholderId, firstIssuanceQty); - issueStock(stockClassId, transferorStakeholderId, secondIssuanceQty); + IStockFacet(address(capTable)).issueStock(params); + } + + function testFullTransfer() public { + // Expect consolidation and transfer events + vm.expectEmit(true, false, false, false, address(capTable)); + emit TxHelper.TxCreated(TxType.STOCK_CONSOLIDATION, ""); // Only check event type + + vm.expectEmit(true, false, false, false, address(capTable)); + emit TxHelper.TxCreated(TxType.STOCK_TRANSFER, ""); // Only check event type + + // Perform full transfer + IStockFacet(address(capTable)).transferStock( + transferorId, + transfereeId, + stockClassId, + INITIAL_SHARES, + SHARE_PRICE * 2 // New price for transfer + ); + + // Verify transferor has no shares + bytes16[] memory transferorSecurities = + IStockFacet(address(capTable)).getStakeholderSecurities(transferorId, stockClassId); + assertEq(transferorSecurities.length, 0, "Transferor should have no securities"); + + // Verify transferee has the shares + bytes16[] memory transfereeSecurities = + IStockFacet(address(capTable)).getStakeholderSecurities(transfereeId, stockClassId); + assertEq(transfereeSecurities.length, 1, "Transferee should have one security"); + + // Check the transferred position + StockActivePosition memory position = IStockFacet(address(capTable)).getStockPosition(transfereeSecurities[0]); + assertEq(position.quantity, INITIAL_SHARES, "Incorrect transfer quantity"); + assertEq(position.share_price, SHARE_PRICE * 2, "Incorrect transfer price"); + } + + function testPartialTransfer() public { + uint256 transferAmount = INITIAL_SHARES / 2; + + // Perform partial transfer + IStockFacet(address(capTable)).transferStock( + transferorId, transfereeId, stockClassId, transferAmount, SHARE_PRICE * 2 + ); + + // Verify transferor's remaining position + bytes16[] memory transferorSecurities = + IStockFacet(address(capTable)).getStakeholderSecurities(transferorId, stockClassId); + assertEq(transferorSecurities.length, 1, "Transferor should have one security"); + + StockActivePosition memory transferorPosition = + IStockFacet(address(capTable)).getStockPosition(transferorSecurities[0]); + assertEq(transferorPosition.quantity, INITIAL_SHARES - transferAmount, "Incorrect remainder quantity"); + assertEq(transferorPosition.share_price, SHARE_PRICE, "Remainder price should not change"); + + // Verify transferee's new position + bytes16[] memory transfereeSecurities = + IStockFacet(address(capTable)).getStakeholderSecurities(transfereeId, stockClassId); + assertEq(transfereeSecurities.length, 1, "Transferee should have one security"); + + StockActivePosition memory transfereePosition = + IStockFacet(address(capTable)).getStockPosition(transfereeSecurities[0]); + assertEq(transfereePosition.quantity, transferAmount, "Incorrect transfer quantity"); + assertEq(transfereePosition.share_price, SHARE_PRICE * 2, "Incorrect transfer price"); + } + + function test_RevertInvalidTransferor() public { + bytes16 invalidTransferorId = bytes16(uint128(transferorId) + 100); + + vm.expectRevert(abi.encodeWithSignature("NoStakeholder(bytes16)", invalidTransferorId)); + IStockFacet(address(capTable)).transferStock( + invalidTransferorId, transfereeId, stockClassId, INITIAL_SHARES, SHARE_PRICE + ); + } + + function test_RevertInvalidTransferee() public { + bytes16 invalidTransfereeId = bytes16(uint128(transfereeId) + 100); + + vm.expectRevert(abi.encodeWithSignature("NoStakeholder(bytes16)", invalidTransfereeId)); + IStockFacet(address(capTable)).transferStock( + transferorId, invalidTransfereeId, stockClassId, INITIAL_SHARES, SHARE_PRICE + ); + } - uint256 totalIssued = firstIssuanceQty + secondIssuanceQty; + function test_RevertInsufficientShares() public { + vm.expectRevert("Insufficient shares for transfer"); + IStockFacet(address(capTable)).transferStock( + transferorId, + transfereeId, + stockClassId, + INITIAL_SHARES + 1, // Try to transfer more than available + SHARE_PRICE + ); + } + + function test_RevertUnauthorizedCaller() public { + // Switch to a non-operator address + address nonOperator = address(0x123); + vm.startPrank(nonOperator); + + vm.expectRevert( + abi.encodeWithSignature( + "AccessControlUnauthorized(address,bytes32)", nonOperator, keccak256("OPERATOR_ROLE") + ) + ); + IStockFacet(address(capTable)).transferStock( + transferorId, transfereeId, stockClassId, INITIAL_SHARES, SHARE_PRICE + ); - return (transferorStakeholderId, transfereeStakeholderId, stockClassId, totalIssued); + vm.stopPrank(); } - function testTransferStockAcrossMultiplePositions() public { - (bytes16 transferorStakeholderId, bytes16 transfereeStakeholderId, bytes16 stockClassId, uint256 totalIssued) = createTransferSetup(); + function testConsolidationHash() public { + // Issue a second position to the same transferor + bytes16 secondSecurityId = bytes16(uint128(transferorId) + 4); + IssueStockParams memory params = IssueStockParams({ + id: bytes16(uint128(transferorId) + 5), + stock_class_id: stockClassId, + share_price: SHARE_PRICE * 2, // Different price + quantity: INITIAL_SHARES, + stakeholder_id: transferorId, + security_id: secondSecurityId, + custom_id: "STOCK_002", + stock_legend_ids_mapping: "LEGEND_1", + security_law_exemptions_mapping: "REG_D" + }); + IStockFacet(address(capTable)).issueStock(params); - // Transfer stock - uint256 quantityToTransfer = 3500; - uint256 price = 25; - capTable.transferStock(transferorStakeholderId, transfereeStakeholderId, stockClassId, true, quantityToTransfer, price); + // Get initial securities + bytes16[] memory initialSecurities = + IStockFacet(address(capTable)).getStakeholderSecurities(transferorId, stockClassId); + assertEq(initialSecurities.length, 2, "Should have two initial securities"); - uint256 transactionsCount = capTable.getTransactionsCount(); - bytes memory lastIssuanceTx = capTable.transactions(transactionsCount - 2); - bytes memory firstTransferTx = capTable.transactions(transactionsCount - 4); - bytes memory secondTransferTx = capTable.transactions(transactionsCount - 1); + // Perform transfer to trigger consolidation + IStockFacet(address(capTable)).transferStock( + transferorId, transfereeId, stockClassId, INITIAL_SHARES, SHARE_PRICE + ); - StockTransfer memory firstTransfer = abi.decode(firstTransferTx, (StockTransfer)); - bytes16 remainingIssuanceSecurityId = abi.decode(lastIssuanceTx, (StockIssuance)).security_id; - StockTransfer memory secondTransfer = abi.decode(secondTransferTx, (StockTransfer)); + // Get resulting securities after consolidation + bytes16[] memory resultingSecurities = + IStockFacet(address(capTable)).getStakeholderSecurities(transferorId, stockClassId); + assertEq(resultingSecurities.length, 1, "Should have one remaining security after partial transfer"); - assertEq(firstTransfer.quantity, 3000); - assertEq(secondTransfer.quantity, 500); - assertEq(secondTransfer.balance_security_id, remainingIssuanceSecurityId); + // The resulting security ID should be different from both initial securities + assertTrue( + resultingSecurities[0] != initialSecurities[0] && resultingSecurities[0] != initialSecurities[1], + "Consolidated security ID should be unique" + ); + } - (, , uint256 shares_issued, ) = capTable.issuer(); + function test_RevertConsolidateEmptyPositions() public { + // Create a stakeholder with no positions + bytes16 emptyStakeholderId = bytes16(uint128(transferorId) + 6); + IStakeholderFacet(address(capTable)).createStakeholder(emptyStakeholderId); - // shares issued should not have changed. - assertEq(shares_issued, totalIssued); + vm.expectRevert(abi.encodeWithSignature("NoPositionsToConsolidate()")); + IStockFacet(address(capTable)).transferStock( + emptyStakeholderId, transfereeId, stockClassId, INITIAL_SHARES, SHARE_PRICE + ); } - function testTransferMoreThanAvailable() public { - (bytes16 transferorStakeholderId, bytes16 transfereeStakeholderId, bytes16 stockClassId, uint256 totalIssued) = createTransferSetup(); + function test_RevertConsolidateZeroQuantityPosition() public { + // First transfer all shares to make position zero + IStockFacet(address(capTable)).transferStock( + transferorId, transfereeId, stockClassId, INITIAL_SHARES, SHARE_PRICE + ); + + // Attempt another transfer with the same transferor + vm.expectRevert(abi.encodeWithSignature("NoPositionsToConsolidate()")); + IStockFacet(address(capTable)).transferStock( + transferorId, transfereeId, stockClassId, INITIAL_SHARES, SHARE_PRICE + ); + } + + // function test_RevertConsolidateMismatchedStockClass() public { + // // Create a different stock class + // bytes16 differentStockClassId = createStockClass(bytes16(uint128(19))); + + // // Issue a position with different stock class + // bytes16 secondSecurityId = bytes16(uint128(transferorId) + 4); + // IssueStockParams memory params = IssueStockParams({ + // id: bytes16(uint128(transferorId) + 5), + // stock_class_id: differentStockClassId, + // share_price: SHARE_PRICE, + // quantity: INITIAL_SHARES, + // stakeholder_id: transferorId, + // security_id: secondSecurityId, + // custom_id: "STOCK_002", + // stock_legend_ids_mapping: "LEGEND_1", + // security_law_exemptions_mapping: "REG_D" + // }); + // IStockFacet(address(capTable)).issueStock(params); + + // console.log("differentStockClassId"); + // console.logBytes16(differentStockClassId); + // vm.expectRevert(abi.encodeWithSignature("StockClassAlreadyExists(bytes16)", differentStockClassId)); + // IStockFacet(address(capTable)).transferStock( + // transferorId, transfereeId, differentStockClassId, INITIAL_SHARES, SHARE_PRICE + // ); + // } + + function testConsolidationWeightedPrice() public { + // Issue a second position with different price + bytes16 secondSecurityId = bytes16(uint128(transferorId) + 4); + IssueStockParams memory params = IssueStockParams({ + id: bytes16(uint128(transferorId) + 5), + stock_class_id: stockClassId, + share_price: SHARE_PRICE * 2, // Double the price + quantity: INITIAL_SHARES * 2, // Double the quantity + stakeholder_id: transferorId, + security_id: secondSecurityId, + custom_id: "STOCK_002", + stock_legend_ids_mapping: "LEGEND_1", + security_law_exemptions_mapping: "REG_D" + }); + IStockFacet(address(capTable)).issueStock(params); + + // Calculate expected weighted average: (1000*100 + 2000*200) / (1000 + 2000) + uint256 expectedPrice = + (INITIAL_SHARES * SHARE_PRICE + INITIAL_SHARES * 2 * SHARE_PRICE * 2) / (INITIAL_SHARES * 3); + + // Trigger consolidation via transfer + IStockFacet(address(capTable)).transferStock( + transferorId, transfereeId, stockClassId, INITIAL_SHARES, SHARE_PRICE + ); + + // Get the remaining position + bytes16[] memory securities = + IStockFacet(address(capTable)).getStakeholderSecurities(transferorId, stockClassId); + assertEq(securities.length, 1, "Should have one remaining security"); + + StockActivePosition memory position = IStockFacet(address(capTable)).getStockPosition(securities[0]); + assertEq(position.share_price, expectedPrice, "Weighted average price should be correct"); + } + + function testConsolidationWithExtremeQuantities() public { + uint256 largeAmount = type(uint128).max; + uint256 veryLargeAmount = type(uint256).max; + + // Adjust issuer authorized shares to handle large quantities + IIssuerFacet(address(capTable)).adjustIssuerAuthorizedShares(issuerId, veryLargeAmount); + IStockClassFacet(address(capTable)).adjustAuthorizedShares( + bytes16(uint128(transferorId) + 100), stockClassId, veryLargeAmount + ); + + bytes16 smallSecurityId = bytes16(uint128(transferorId) + 4); + IssueStockParams memory smallParams = IssueStockParams({ + id: bytes16(uint128(transferorId) + 5), + stock_class_id: stockClassId, + share_price: SHARE_PRICE, + quantity: 1, // Minimum quantity + stakeholder_id: transferorId, + security_id: smallSecurityId, + custom_id: "STOCK_SMALL", + stock_legend_ids_mapping: "LEGEND_1", + security_law_exemptions_mapping: "REG_D" + }); + IStockFacet(address(capTable)).issueStock(smallParams); + + // Issue a position with large quantity + bytes16 largeSecurityId = bytes16(uint128(transferorId) + 6); + IssueStockParams memory largeParams = IssueStockParams({ + id: bytes16(uint128(transferorId) + 7), + stock_class_id: stockClassId, + share_price: SHARE_PRICE, + quantity: largeAmount, + stakeholder_id: transferorId, + security_id: largeSecurityId, + custom_id: "STOCK_LARGE", + stock_legend_ids_mapping: "LEGEND_1", + security_law_exemptions_mapping: "REG_D" + }); + IStockFacet(address(capTable)).issueStock(largeParams); + + // Trigger consolidation by transferring the smallest amount + IStockFacet(address(capTable)).transferStock(transferorId, transfereeId, stockClassId, 1, SHARE_PRICE); + + // Verify the consolidated position + bytes16[] memory securities = + IStockFacet(address(capTable)).getStakeholderSecurities(transferorId, stockClassId); + StockActivePosition memory position = IStockFacet(address(capTable)).getStockPosition(securities[0]); - // Transfer stock - uint256 quantityToTransfer = 5500; - uint256 price = 25; + // Calculate expected: initial + small + large - transferred + uint256 expectedQuantity = INITIAL_SHARES + largeAmount; // Add the large amount first + expectedQuantity = expectedQuantity + 1 - 1; // Then add small and subtract transferred amount - bytes memory expectedError = abi.encodeWithSignature("InsufficientShares(uint256,uint256)", totalIssued, quantityToTransfer); - vm.expectRevert(expectedError); - capTable.transferStock(transferorStakeholderId, transfereeStakeholderId, stockClassId, true, quantityToTransfer, price); + assertEq(position.quantity, expectedQuantity, "Should handle large quantities correctly"); } } diff --git a/chain/test/SyncFacets.t.sol b/chain/test/SyncFacets.t.sol new file mode 100644 index 00000000..d7b632bd --- /dev/null +++ b/chain/test/SyncFacets.t.sol @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "forge-std/Test.sol"; +import { MockFacet, MockFacetV2 } from "./mocks/MockFacet.sol"; +import { SyncFacetsScript, FacetHelper } from "../script/SyncFacets.s.sol"; +import { LibDeployment } from "../script/DeployFactory.s.sol"; +import { IDiamondLoupe } from "diamond-3-hardhat/interfaces/IDiamondLoupe.sol"; + +contract SyncFacetsTest is Test, SyncFacetsScript { + MockFacet public mockFacet; + MockFacetV2 public mockFacetV2; + address public contractOwner; + address public localDiamond; + address public remoteDiamond; + + function setUp() public { + contractOwner = address(this); + + // Deploy two separate diamonds for comparison + localDiamond = LibDeployment.deployInitialFacets(contractOwner); + remoteDiamond = LibDeployment.deployInitialFacets(contractOwner); + + // Deploy mock facets + mockFacet = new MockFacet(); + mockFacetV2 = new MockFacetV2(); + } + + enum MockFacetType { + MockFacet, + MockFacetV2 + } + + function getFacetCutInfo(MockFacetType facetType) internal pure returns (LibDeployment.FacetCutInfo memory info) { + if (facetType == MockFacetType.MockFacet) { + bytes4[] memory selectors = new bytes4[](1); + selectors[0] = MockFacet.getValuePlusOne.selector; + return LibDeployment.FacetCutInfo({ name: "MockFacet", selectors: selectors }); + } + if (facetType == MockFacetType.MockFacetV2) { + bytes4[] memory selectors = new bytes4[](1); + selectors[0] = MockFacetV2.getValuePlusTwo.selector; + return LibDeployment.FacetCutInfo({ name: "MockFacetV2", selectors: selectors }); + } + revert("Unknown facet type"); + } + + function testDetectNoChanges() public { + // Get facets from both diamonds + IDiamondLoupe.Facet[] memory localFacets = IDiamondLoupe(localDiamond).facets(); + IDiamondLoupe.Facet[] memory remoteFacets = IDiamondLoupe(remoteDiamond).facets(); + + // Get hashes + FacetHelper.BytecodeHash[] memory localHashes = FacetHelper.getHashes(localFacets); + FacetHelper.BytecodeHash[] memory remoteHashes = FacetHelper.getHashes(remoteFacets); + + // Detect changes + (, uint256 changeCount) = FacetHelper.detectChanges(localFacets, remoteFacets, localHashes, remoteHashes); + + assertEq(changeCount, 0, "Should detect no changes between identical diamonds"); + } + + function testDetectAddedFacet() public { + // Add mock facet to local diamond only + bytes4[] memory selectors = getFacetCutInfo(MockFacetType.MockFacet).selectors; + addFacet(localDiamond, address(mockFacet), selectors); + + // Get facets from both diamonds + IDiamondLoupe.Facet[] memory localFacets = IDiamondLoupe(localDiamond).facets(); + IDiamondLoupe.Facet[] memory remoteFacets = IDiamondLoupe(remoteDiamond).facets(); + + // Get hashes + FacetHelper.BytecodeHash[] memory localHashes = FacetHelper.getHashes(localFacets); + FacetHelper.BytecodeHash[] memory remoteHashes = FacetHelper.getHashes(remoteFacets); + + // Detect changes + (FacetHelper.FacetChange[] memory changes, uint256 changeCount) = + FacetHelper.detectChanges(localFacets, remoteFacets, localHashes, remoteHashes); + + assertTrue(changeCount > 0, "Should detect added facet"); + assertEq(uint8(changes[0].changeType), uint8(FacetHelper.ChangeType.Add), "Should be an Add change"); + } + + function testDetectUpdatedFacet() public { + // First add the same facet to both diamonds + bytes4[] memory selectors = getFacetCutInfo(MockFacetType.MockFacet).selectors; + + console.log("Adding to local diamond:", address(mockFacet)); + addFacet(localDiamond, address(mockFacet), selectors); + + console.log("Adding to remote diamond:", address(mockFacet)); + addFacet(remoteDiamond, address(mockFacet), selectors); + + console.log("Replacing in local diamond with:", address(mockFacetV2)); + replaceFacet(localDiamond, address(mockFacetV2), selectors); + + // Get facets and verify they exist + IDiamondLoupe.Facet[] memory localFacets = IDiamondLoupe(localDiamond).facets(); + IDiamondLoupe.Facet[] memory remoteFacets = IDiamondLoupe(remoteDiamond).facets(); + + // Get hashes + FacetHelper.BytecodeHash[] memory localHashes = FacetHelper.getHashes(localFacets); + FacetHelper.BytecodeHash[] memory remoteHashes = FacetHelper.getHashes(remoteFacets); + + // Print hashes for debugging + for (uint256 i = 0; i < localHashes.length; i++) { + console.log("Local hash for", localHashes[i].facetAddress, ":"); + console.logBytes32(localHashes[i].hash); + } + + // Detect changes + (FacetHelper.FacetChange[] memory changes, uint256 changeCount) = + FacetHelper.detectChanges(localFacets, remoteFacets, localHashes, remoteHashes); + + console.log("changeCount", changeCount); + + assertTrue(changeCount > 0, "Should detect updated facet"); + assertEq(uint8(changes[0].changeType), uint8(FacetHelper.ChangeType.Update), "Should be an Update change"); + assertEq(changes[0].currentAddress, address(mockFacet), "Current address should be original facet"); + assertEq(changes[0].newAddress, address(mockFacetV2), "New address should be updated facet"); + } + + function testDetectRemovedFacet() public { + // Add facet only to remote diamond (so it will be detected as needing removal) + bytes4[] memory selectors = getFacetCutInfo(MockFacetType.MockFacet).selectors; + addFacet(remoteDiamond, address(mockFacet), selectors); + + // Get facets from both diamonds + IDiamondLoupe.Facet[] memory localFacets = IDiamondLoupe(localDiamond).facets(); + IDiamondLoupe.Facet[] memory remoteFacets = IDiamondLoupe(remoteDiamond).facets(); + + // Get hashes + FacetHelper.BytecodeHash[] memory localHashes = FacetHelper.getHashes(localFacets); + FacetHelper.BytecodeHash[] memory remoteHashes = FacetHelper.getHashes(remoteFacets); + + // Detect changes + (FacetHelper.FacetChange[] memory changes, uint256 changeCount) = + FacetHelper.detectChanges(localFacets, remoteFacets, localHashes, remoteHashes); + + assertTrue(changeCount > 0, "Should detect removed facet"); + assertEq(uint8(changes[0].changeType), uint8(FacetHelper.ChangeType.Remove), "Should be a Remove change"); + assertEq(changes[0].currentAddress, address(mockFacet), "Current address should be the facet to remove"); + } + + function testEndToEndSync() public { + // Add mock facet to local diamond + bytes4[] memory selectors = getFacetCutInfo(MockFacetType.MockFacetV2).selectors; + + console.log("Adding to local diamond:", address(mockFacetV2)); + addFacet(localDiamond, address(mockFacetV2), selectors); + + // Get initial state + IDiamondLoupe.Facet[] memory localFacets = IDiamondLoupe(localDiamond).facets(); + IDiamondLoupe.Facet[] memory remoteFacets = IDiamondLoupe(remoteDiamond).facets(); + + // Get hashes + FacetHelper.BytecodeHash[] memory localHashes = FacetHelper.getHashes(localFacets); + FacetHelper.BytecodeHash[] memory remoteHashes = FacetHelper.getHashes(remoteFacets); + + // Detect changes + (FacetHelper.FacetChange[] memory changes, uint256 changeCount) = + FacetHelper.detectChanges(localFacets, remoteFacets, localHashes, remoteHashes); + + // Process changes + assertTrue(changeCount > 0, "Should detect changes"); + + for (uint256 i = 0; i < changeCount; i++) { + processChanges(changes[i], remoteDiamond, remoteFacets, localFacets); + } + + // Verify sync + IDiamondLoupe.Facet[] memory updatedRemoteFacets = IDiamondLoupe(remoteDiamond).facets(); + FacetHelper.BytecodeHash[] memory updatedRemoteHashes = FacetHelper.getHashes(updatedRemoteFacets); + + // Check no more changes needed + (, uint256 remainingChanges) = + FacetHelper.detectChanges(localFacets, updatedRemoteFacets, localHashes, updatedRemoteHashes); + + assertEq(remainingChanges, 0, "Should have no remaining changes after sync"); + + // Test functionality through interface + bytes memory calldata1 = abi.encodeWithSelector(MockFacetV2.getValuePlusTwo.selector); + (bool success, bytes memory result) = remoteDiamond.call(calldata1); + require(success, "Call failed"); + assertEq(abi.decode(result, (uint256)), 2, "getValuePlusTwo should return 2"); + } +} diff --git a/chain/test/TestBase.sol b/chain/test/TestBase.sol index df8d44c3..6bc5cd86 100644 --- a/chain/test/TestBase.sol +++ b/chain/test/TestBase.sol @@ -4,38 +4,38 @@ pragma solidity ^0.8.0; import "forge-std/Test.sol"; import "forge-std/console.sol"; import "@core/CapTable.sol"; -import "@facets/IssuerFacet.sol"; -import { StakeholderFacet } from "@facets/StakeholderFacet.sol"; -import { StockClassFacet } from "@facets/StockClassFacet.sol"; -import { StockFacet } from "@facets/StockFacet.sol"; -import { ConvertiblesFacet } from "@facets/ConvertiblesFacet.sol"; -import { EquityCompensationFacet } from "@facets/EquityCompensationFacet.sol"; -import { StockPlanFacet } from "@facets/StockPlanFacet.sol"; +import { CapTableFactory } from "@core/CapTableFactory.sol"; +import { IIssuerFacet } from "@interfaces/IIssuerFacet.sol"; +import { IStakeholderFacet } from "@interfaces/IStakeholderFacet.sol"; +import { IStockClassFacet } from "@interfaces/IStockClassFacet.sol"; +import { IStockPlanFacet } from "@interfaces/IStockPlanFacet.sol"; +import { IAccessControlFacet } from "@interfaces/IAccessControlFacet.sol"; import "diamond-3-hardhat/facets/DiamondCutFacet.sol"; +import "diamond-3-hardhat/facets/DiamondLoupeFacet.sol"; import "diamond-3-hardhat/interfaces/IDiamondCut.sol"; -import { WarrantFacet } from "@facets/WarrantFacet.sol"; -import { StakeholderNFTFacet } from "@facets/StakeholderNFTFacet.sol"; +import { AccessControl } from "@libraries/AccessControl.sol"; +import { LibDeployment } from "../script/DeployFactory.s.sol"; +import { IStockFacet } from "@interfaces/IStockFacet.sol"; +import { IConvertiblesFacet } from "@interfaces/IConvertiblesFacet.sol"; +import { IEquityCompensationFacet } from "@interfaces/IEquityCompensationFacet.sol"; +import { IWarrantFacet } from "@interfaces/IWarrantFacet.sol"; +import { IStakeholderNFTFacet } from "@interfaces/IStakeholderNFTFacet.sol"; contract DiamondTestBase is Test { - uint256 public issuerInitialSharesAuthorized = 1000000; + uint256 public issuerInitialSharesAuthorized = 1_000_000; bytes16 public issuerId = 0xd3373e0a4dd9430f8a563281f2800e1e; address public contractOwner; - - DiamondCutFacet public diamondCutFacet; - IssuerFacet public issuerFacet; - StakeholderFacet public stakeholderFacet; - StockClassFacet public stockClassFacet; - StockFacet public stockFacet; - ConvertiblesFacet public convertiblesFacet; - EquityCompensationFacet public equityCompensationFacet; - StockPlanFacet public stockPlanFacet; + address public referenceDiamond; CapTable public capTable; - WarrantFacet public warrantFacet; - StakeholderNFTFacet public stakeholderNFTFacet; + CapTableFactory public factory; - event StockIssued(bytes16 indexed stakeholderId, bytes16 indexed stockClassId, uint256 quantity, uint256 sharePrice); + event StockIssued( + bytes16 indexed stakeholderId, bytes16 indexed stockClassId, uint256 quantity, uint256 sharePrice + ); event StakeholderCreated(bytes16 indexed id); - event StockClassCreated(bytes16 indexed id, string indexed classType, uint256 indexed pricePerShare, uint256 initialSharesAuthorized); + event StockClassCreated( + bytes16 indexed id, string indexed classType, uint256 indexed pricePerShare, uint256 initialSharesAuthorized + ); event StockPlanCreated(bytes16 indexed id, uint256 shares_reserved); // TOOD: figure out if should use the facets' events? event IssuerAuthorizedSharesAdjusted(uint256 newSharesAuthorized); @@ -45,157 +45,43 @@ contract DiamondTestBase is Test { function setUp() public virtual { contractOwner = address(this); - // Deploy facets - diamondCutFacet = new DiamondCutFacet(); - issuerFacet = new IssuerFacet(); - capTable = new CapTable(contractOwner, address(diamondCutFacet)); - stakeholderFacet = new StakeholderFacet(); - stockClassFacet = new StockClassFacet(); - stockFacet = new StockFacet(); - convertiblesFacet = new ConvertiblesFacet(); - equityCompensationFacet = new EquityCompensationFacet(); - stockPlanFacet = new StockPlanFacet(); - warrantFacet = new WarrantFacet(); - stakeholderNFTFacet = new StakeholderNFTFacet(); - - // Add facets - IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](9); - - bytes4[] memory issuerSelectors = new bytes4[](2); - issuerSelectors[0] = IssuerFacet.initializeIssuer.selector; - issuerSelectors[1] = IssuerFacet.adjustIssuerAuthorizedShares.selector; - - bytes4[] memory stakeholderSelectors = new bytes4[](3); - stakeholderSelectors[0] = StakeholderFacet.createStakeholder.selector; - stakeholderSelectors[1] = StakeholderFacet.getStakeholderPositions.selector; - stakeholderSelectors[2] = StakeholderFacet.linkStakeholderAddress.selector; - - bytes4[] memory stockClassSelectors = new bytes4[](2); - stockClassSelectors[0] = StockClassFacet.createStockClass.selector; - stockClassSelectors[1] = StockClassFacet.adjustAuthorizedShares.selector; - - bytes4[] memory stockSelectors = new bytes4[](1); - stockSelectors[0] = StockFacet.issueStock.selector; - - bytes4[] memory convertibleSelectors = new bytes4[](2); - convertibleSelectors[0] = ConvertiblesFacet.issueConvertible.selector; - convertibleSelectors[1] = ConvertiblesFacet.getConvertiblePosition.selector; - - bytes4[] memory equityCompensationSelectors = new bytes4[](3); - equityCompensationSelectors[0] = EquityCompensationFacet.issueEquityCompensation.selector; - equityCompensationSelectors[1] = EquityCompensationFacet.getPosition.selector; - equityCompensationSelectors[2] = EquityCompensationFacet.exerciseEquityCompensation.selector; - - bytes4[] memory stockPlanSelectors = new bytes4[](2); - stockPlanSelectors[0] = StockPlanFacet.createStockPlan.selector; - stockPlanSelectors[1] = StockPlanFacet.adjustStockPlanPool.selector; - - bytes4[] memory warrantSelectors = new bytes4[](2); - warrantSelectors[0] = WarrantFacet.issueWarrant.selector; - warrantSelectors[1] = WarrantFacet.getWarrantPosition.selector; - - bytes4[] memory nftSelectors = new bytes4[](2); - nftSelectors[0] = StakeholderNFTFacet.mint.selector; - nftSelectors[1] = StakeholderNFTFacet.tokenURI.selector; - - // issuer facet - cut[0] = IDiamondCut.FacetCut({ - facetAddress: address(issuerFacet), - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: issuerSelectors - }); - - // stakeholder facet - cut[1] = IDiamondCut.FacetCut({ - facetAddress: address(stakeholderFacet), - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: stakeholderSelectors - }); - - // stock class facet - cut[2] = IDiamondCut.FacetCut({ - facetAddress: address(stockClassFacet), - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: stockClassSelectors - }); - // stock facet - cut[3] = IDiamondCut.FacetCut({ - facetAddress: address(stockFacet), - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: stockSelectors - }); - - // convertible facet - cut[4] = IDiamondCut.FacetCut({ - facetAddress: address(convertiblesFacet), - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: convertibleSelectors - }); - - // equity facet - cut[5] = IDiamondCut.FacetCut({ - facetAddress: address(equityCompensationFacet), - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: equityCompensationSelectors - }); - - // stock plan facet - cut[6] = IDiamondCut.FacetCut({ - facetAddress: address(stockPlanFacet), - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: stockPlanSelectors - }); - - // warrant facet - cut[7] = IDiamondCut.FacetCut({ - facetAddress: address(warrantFacet), - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: warrantSelectors - }); - - // NFT facet - cut[8] = IDiamondCut.FacetCut({ - facetAddress: address(stakeholderNFTFacet), - action: IDiamondCut.FacetCutAction.Add, - functionSelectors: nftSelectors - }); - - DiamondCutFacet(address(capTable)).diamondCut(cut, address(0), ""); - - // Initialize issuer - IssuerFacet(payable(address(capTable))).initializeIssuer(issuerId, issuerInitialSharesAuthorized); + // Use the deployment script's function + referenceDiamond = LibDeployment.deployInitialFacets(contractOwner); + + // Create factory using reference diamond + factory = new CapTableFactory(referenceDiamond); + + // Create a new cap table for testing + capTable = CapTable(payable(factory.createCapTable(issuerId, issuerInitialSharesAuthorized))); + console.log("capTable: ", address(capTable)); + IAccessControlFacet(address(capTable)).acceptAdmin(); } // Common helper functions - function createStakeholder() public returns (bytes16) { + function createStakeholder() public virtual returns (bytes16) { bytes16 stakeholderId = 0xd3373e0a4dd940000000000000000005; - // Debug log before creation - console.log("Before creation - index:", StorageLib.get().stakeholderIndex[stakeholderId]); - vm.expectEmit(true, false, false, false, address(capTable)); emit StakeholderCreated(stakeholderId); - // Call through the diamond proxy instead of using delegatecall - StakeholderFacet(address(capTable)).createStakeholder(stakeholderId); - - // Debug log after creation - console.log("After creation - index:", StorageLib.get().stakeholderIndex[stakeholderId]); + IStakeholderFacet(address(capTable)).createStakeholder(stakeholderId); return stakeholderId; } // Helper function to create a stock class for testing - function createStockClass() public returns (bytes16) { - bytes16 stockClassId = 0xd3373e0a4dd940000000000000000006; + function createStockClass(bytes16 stockClassId) public virtual returns (bytes16) { + // bytes16 stockClassId = 0xd3373e0a4dd940000000000000000019; string memory classType = "COMMON"; uint256 pricePerShare = 1e18; - uint256 initialSharesAuthorized = 1000000; + uint256 initialSharesAuthorized = 1_000_000; vm.expectEmit(true, true, true, true, address(capTable)); emit StockClassCreated(stockClassId, classType, pricePerShare, initialSharesAuthorized); - StockClassFacet(payable(address(capTable))).createStockClass(stockClassId, classType, pricePerShare, initialSharesAuthorized); + IStockClassFacet(address(capTable)).createStockClass( + stockClassId, classType, pricePerShare, initialSharesAuthorized + ); return stockClassId; } @@ -203,18 +89,18 @@ contract DiamondTestBase is Test { // Helper function to create a stock plan for testing function createStockPlan(bytes16[] memory stockClassIds) public returns (bytes16) { bytes16 stockPlanId = 0xd3373e0a4dd940000000000000000007; - uint256 sharesReserved = 100000; + uint256 sharesReserved = 100_000; vm.expectEmit(true, false, false, true, address(capTable)); emit StockPlanCreated(stockPlanId, sharesReserved); - StockPlanFacet(payable(address(capTable))).createStockPlan(stockPlanId, stockClassIds, sharesReserved); + IStockPlanFacet(address(capTable)).createStockPlan(stockPlanId, stockClassIds, sharesReserved); return stockPlanId; } // Add this helper function alongside the other helpers function linkStakeholderAddress(bytes16 _stakeholderId, address _wallet) public { - StakeholderFacet(payable(address(capTable))).linkStakeholderAddress(_stakeholderId, _wallet); + IStakeholderFacet(address(capTable)).linkStakeholderAddress(_stakeholderId, _wallet); } } diff --git a/chain/test/Wallet.t.sol b/chain/test/Wallet.t.sol deleted file mode 100644 index 544f11e8..00000000 --- a/chain/test/Wallet.t.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "./CapTable.t.sol"; - -contract WalletTest is CapTableTest { - function testAddWalletToStakeholder() public { - (, bytes16 stakeholderId) = createStockClassAndStakeholder(10000000); - - address wallet = address(0x122); - - capTable.addWalletToStakeholder(stakeholderId, wallet); - - bytes16 actualStakeholderId = capTable.getStakeholderIdByWallet(wallet); - - assertEq(actualStakeholderId, stakeholderId); - } - - function testRemoveWalletFromStakeholder() public { - (, bytes16 stakeholderId) = createStockClassAndStakeholder(10000000); - - address wallet = address(0x122); - - capTable.addWalletToStakeholder(stakeholderId, wallet); - - bytes16 actualStakeholderId = capTable.getStakeholderIdByWallet(wallet); - - assertEq(actualStakeholderId, stakeholderId); - - capTable.removeWalletFromStakeholder(stakeholderId, wallet); - - vm.expectRevert("No stakeholder found"); - - capTable.getStakeholderIdByWallet(wallet); - } - - function testInvalidZeroWalletReverts() public { - (, bytes16 stakeholderId) = createStockClassAndStakeholder(10000000); - vm.expectRevert(abi.encodeWithSignature("InvalidWallet(address)", address(0))); - capTable.addWalletToStakeholder(stakeholderId, address(0)); - } - - function testDuplicateWalletReverts() public { - (, bytes16 stakeholderId) = createStockClassAndStakeholder(10000000); - address wallet = address(0x122); - capTable.addWalletToStakeholder(stakeholderId, wallet); - vm.expectRevert(abi.encodeWithSignature("WalletAlreadyExists(address)", wallet)); - capTable.addWalletToStakeholder(stakeholderId, wallet); - } -} diff --git a/chain/test/WarrantIssuance.t.sol b/chain/test/WarrantIssuance.t.sol index a9b08400..583eafb5 100644 --- a/chain/test/WarrantIssuance.t.sol +++ b/chain/test/WarrantIssuance.t.sol @@ -5,38 +5,72 @@ import "./TestBase.sol"; import { StorageLib } from "@core/Storage.sol"; import { TxHelper, TxType } from "@libraries/TxHelper.sol"; import { ValidationLib } from "@libraries/ValidationLib.sol"; -import { WarrantActivePosition } from "@libraries/Structs.sol"; +import { WarrantActivePosition, IssueWarrantParams } from "@libraries/Structs.sol"; +import { IWarrantFacet } from "@interfaces/IWarrantFacet.sol"; contract DiamondWarrantIssuanceTest is DiamondTestBase { function testIssueWarrant() public { bytes16 stakeholderId = createStakeholder(); uint256 quantity = 1000; bytes16 securityId = 0xd3373e0a4dd940000000000000000001; + bytes16 id = 0xd3373e0a4dd940000000000000000002; + IssueWarrantParams memory params = IssueWarrantParams({ + id: id, + stakeholder_id: stakeholderId, + quantity: quantity, + security_id: securityId, + purchase_price: 1e18, + custom_id: "WARRANT_001", + security_law_exemptions_mapping: "REG_D", + exercise_triggers_mapping: "TIME_BASED" + }); vm.expectEmit(true, true, false, true, address(capTable)); - emit TxHelper.TxCreated(TxType.WARRANT_ISSUANCE, abi.encode(stakeholderId, quantity, securityId)); + emit TxHelper.TxCreated(TxType.WARRANT_ISSUANCE, abi.encode(params)); - WarrantFacet(address(capTable)).issueWarrant(stakeholderId, quantity, securityId); + IWarrantFacet(address(capTable)).issueWarrant(params); // Verify position was created correctly - WarrantActivePosition memory position = WarrantFacet(address(capTable)).getWarrantPosition(securityId); + WarrantActivePosition memory position = IWarrantFacet(address(capTable)).getWarrantPosition(securityId); assertEq(position.quantity, quantity); assertEq(position.stakeholder_id, stakeholderId); } - function testFailInvalidStakeholder() public { + function test_RevertInvalidStakeholder() public { bytes16 invalidStakeholderId = 0xd3373e0a4dd940000000000000000099; bytes16 securityId = 0xd3373e0a4dd940000000000000000001; + bytes16 id = 0xd3373e0a4dd940000000000000000002; - // Just let it fail without expectRevert - WarrantFacet(address(capTable)).issueWarrant(invalidStakeholderId, 1000, securityId); + IssueWarrantParams memory params = IssueWarrantParams({ + id: id, + stakeholder_id: invalidStakeholderId, + quantity: 1000, + security_id: securityId, + purchase_price: 1e18, + custom_id: "WARRANT_002", + security_law_exemptions_mapping: "REG_D", + exercise_triggers_mapping: "TIME_BASED" + }); + vm.expectRevert(abi.encodeWithSignature("NoStakeholder(bytes16)", invalidStakeholderId)); + IWarrantFacet(address(capTable)).issueWarrant(params); } - function testFailZeroQuantity() public { + function test_RevertZeroQuantity() public { bytes16 stakeholderId = createStakeholder(); bytes16 securityId = 0xd3373e0a4dd940000000000000000001; + bytes16 id = 0xd3373e0a4dd940000000000000000002; - // Just let it fail without expectRevert - WarrantFacet(address(capTable)).issueWarrant(stakeholderId, 0, securityId); + IssueWarrantParams memory params = IssueWarrantParams({ + id: id, + stakeholder_id: stakeholderId, + quantity: 0, + security_id: securityId, + purchase_price: 1e18, + custom_id: "WARRANT_003", + security_law_exemptions_mapping: "REG_D", + exercise_triggers_mapping: "TIME_BASED" + }); + vm.expectRevert(abi.encodeWithSignature("InvalidQuantity()")); + IWarrantFacet(address(capTable)).issueWarrant(params); } } diff --git a/chain/test/mocks/MockFacet.sol b/chain/test/mocks/MockFacet.sol new file mode 100644 index 00000000..329ae51c --- /dev/null +++ b/chain/test/mocks/MockFacet.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract MockFacet { + uint256 private value; + + function getValuePlusOne() external view returns (uint256) { + return value + 1; + } +} + +contract MockFacetV2 { + uint256 private value; + + function getValuePlusTwo() external view returns (uint256) { + return value + 2; + } +} diff --git a/docker-compose.yml b/docker-compose.yml index 25884aa5..31109fe7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,14 +1,14 @@ version: "3.7" services: - mongodb_container: - # This image automatically creates a replica set required for transactions - image: mongo:latest - restart: always - environment: - MONGO_INITDB_ROOT_USERNAME: ocp - MONGO_INITDB_ROOT_PASSWORD: ocp - INIT_WAIT_SEC: 3 - ports: - - 27017:27017 - # Make sure log colors show up correctly - tty: true + mongodb_container: + # This image automatically creates a replica set required for transactions + image: mongo:latest + restart: always + environment: + MONGO_INITDB_ROOT_USERNAME: ocp + MONGO_INITDB_ROOT_PASSWORD: ocp + INIT_WAIT_SEC: 3 + ports: + - 27017:27017 + # Make sure log colors show up correctly + tty: true diff --git a/package.json b/package.json index 419470fd..dbe9afcb 100644 --- a/package.json +++ b/package.json @@ -4,27 +4,34 @@ "private": true, "author": "Victor Mimo, Adam Momen, Fairmint Inc., Plural Everything Inc.", "license": "MIT", - "description": "Open Cap Table Protocol", + "description": "Open Cap Table Protocol, fairmint.com fork", "type": "module", "scripts": { + "validate": "npx tsx src/scripts/validate.js", "migrate": "npx tsx src/scripts/migrate.js", - "prod": "npx tsx src/app.js", + "start": "npx tsx src/app.js", "dev": "npx tsx watch src/app.js", "format": "prettier --write \"src/**/*.{js,mjs,ts,mts,json,md}\"", "format:check": "prettier --check \"src/**/*.{js,mjs,ts,mts,json,md}\"", - "lint": "eslint \"src/**/*.{js,ts,mjs,mts}\"", + "lint": "eslint \"src/**/*.{js,ts,mjs,mts}\" --fix", "lint:check": "eslint \"src/**/*.{js,ts,mjs,mts}\"", - "flightcheck": "yarn lint && yarn format:check", + "flightcheck": "yarn lint:check && yarn format:check", "typecheck": "concurrently --raw yarn:typecheck:*", "typecheck:app": "tsc --noEmit", "prepare": "husky", - "deploy-factory": "./scripts/deployFactory.sh", - "setup": "sh forge_install.sh", + "verify:prod": "sh ./scripts/verify_contracts.sh --env=prod", + "deploy:local": "sh ./scripts/deploy_factory.sh --env=local", + "deploy:testnet": "sh ./scripts/deploy_factory.sh --env=dev", + "deploy:mainnet": "sh ./scripts/deploy_factory.sh --env=prod", + "sync:local": "sh ./scripts/sync.sh --env=local", + "sync:testnet": "sh ./scripts/sync.sh --env=dev", + "sync:mainnet": "sh ./scripts/sync.sh --env=prod", + "accept-transfer": "sh ./scripts/acceptTransfer.sh", + "setup": "sh setup.sh", "deseed": "npx tsx src/db/scripts/deseed.js", - "test": "cd chain && forge test", + "test": "cd chain && forge test --summary && cd ..", "test-js": "jest --testPathPattern src/tests/unit", - "test-js-integration": "jest --testPathPattern src/tests/integration", - "export-manifest": "cd src/db/samples && zip -r fairmint.zip fairmint && mv fairmint.zip $HOME/Downloads" + "test-js-integration": "jest --testPathPattern src/tests/integration" }, "dependencies": { "@sentry/node": "^8.37.1", @@ -60,6 +67,7 @@ "@types/uuid": "^9.0.2", "@typescript-eslint/eslint-plugin": "^6.19.0", "@typescript-eslint/parser": "^6.19.0", + "chalk": "4", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "^2.31.0", diff --git a/scripts/acceptTransfer.sh b/scripts/acceptTransfer.sh new file mode 100755 index 00000000..355c46cb --- /dev/null +++ b/scripts/acceptTransfer.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Accept a single argument of an env file to use. By default use .env at root +USE_ENV_FILE=${1:-.env} +echo "USE_ENV_FILE=$USE_ENV_FILE" + +# Check if .env file exists +if [ ! -f "$USE_ENV_FILE" ]; then + echo "Error: Environment file $USE_ENV_FILE does not exist" + exit 1 +fi + +# Check if file is readable +if [ ! -r "$USE_ENV_FILE" ]; then + echo "Error: Environment file $USE_ENV_FILE is not readable" + exit 1 +fi + +# Export environment variables from .env file +while IFS= read -r line || [[ -n "$line" ]]; do + # Skip empty lines and comments + if [[ -z "$line" ]] || [[ "$line" =~ ^# ]]; then + continue + fi + # Remove any quotes and export the variable + line=$(echo "$line" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + export "${line?}" +done < "$USE_ENV_FILE" + +# Copy the root .env underneath chain so we dont have to maintain two copies +TEMP=$PWD/chain/.env +cp $USE_ENV_FILE $TEMP +trap "rm $TEMP" EXIT + +set -x +cd chain +forge script script/AcceptAdminTransfers.s.sol --broadcast --fork-url localhost:8545 --private-key $PRIVATE_KEY \ No newline at end of file diff --git a/scripts/deployFactory.sh b/scripts/deployFactory.sh deleted file mode 100755 index 03763089..00000000 --- a/scripts/deployFactory.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -# Accept a single argument of an env file to use. By default use .env at root -USE_ENV_FILE=${1:-.env} - -source $USE_ENV_FILE - -# Copy the root .env underneath chain so we dont have to maintain two copies -TEMP=$PWD/chain/.env -cp $USE_ENV_FILE $TEMP -trap "rm $TEMP" EXIT - -set -x -cd chain -forge script script/CapTableFactory.s.sol --broadcast --fork-url $RPC_URL diff --git a/scripts/deploy_factory.sh b/scripts/deploy_factory.sh new file mode 100755 index 00000000..9fdf255e --- /dev/null +++ b/scripts/deploy_factory.sh @@ -0,0 +1,165 @@ +#!/bin/bash + +# Sets default environment to "local" if no environment is specified +ENVIRONMENT="local" + +# Processes command line arguments +# Example: ./deploy_factory.local.sh --env=dev +while [[ "$#" -gt 0 ]]; do + case $1 in + --env=*) ENVIRONMENT="${1#*=}" ;; # Extracts value after --env= + *) echo "Unknown parameter: $1"; exit 1 ;; + esac + shift +done + + +# Constructs env file path based on environment +# Example: .env.local, .env.dev, .env.prod +USE_ENV_FILE=".env.$ENVIRONMENT" + +# Exits if the environment file doesn't exist +[ ! -f "$USE_ENV_FILE" ] && echo "Error: $USE_ENV_FILE does not exist" && exit 1 + +# Loads environment variables from the env file +# set -a: automatically exports all variables +# source: loads the env file +# set +a: stops auto-exporting +set -a +source "$USE_ENV_FILE" +set +a + +# Creates a temporary copy of env file in the chain directory +# TEMP will be something like /your/path/chain/.env +TEMP=$PWD/chain/.env +cp "$USE_ENV_FILE" "$TEMP" +# Removes the temporary file when script exits +trap "rm $TEMP" EXIT + +# Add confirmation step for non-local environments +if [ "$ENVIRONMENT" != "local" ]; then + echo "⚠️ You are about to deploy to $ENVIRONMENT environment" + echo "RPC URL will be: $RPC_URL" + read -p "Are you sure you want to continue? (y/N) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Deployment cancelled" + exit 1 + fi +fi + +# Changes to chain directory and runs the forge deploy script +cd chain +echo $RPC_URL +echo $CHAIN_ID + +# Deploy contracts +DEPLOY_OUTPUT=$(forge script script/DeployFactory.s.sol --broadcast --rpc-url $RPC_URL --private-key $PRIVATE_KEY --chain-id $CHAIN_ID) +echo "$DEPLOY_OUTPUT" + +# Extract all addresses +FACTORY_ADDRESS=$(echo "$DEPLOY_OUTPUT" | grep "FACTORY_ADDRESS=" | cut -d'=' -f2 | tr -d ' ') +REFERENCE_DIAMOND=$(echo "$DEPLOY_OUTPUT" | grep "REFERENCE_DIAMOND=" | cut -d'=' -f2 | tr -d ' ') +DIAMOND_LOUPE_FACET=$(echo "$DEPLOY_OUTPUT" | grep "DIAMOND_LOUPE_FACET=" | cut -d'=' -f2 | tr -d ' ') +ISSUER_FACET=$(echo "$DEPLOY_OUTPUT" | grep "ISSUER_FACET=" | cut -d'=' -f2 | tr -d ' ') +STAKEHOLDER_FACET=$(echo "$DEPLOY_OUTPUT" | grep "STAKEHOLDER_FACET=" | cut -d'=' -f2 | tr -d ' ') +STOCK_CLASS_FACET=$(echo "$DEPLOY_OUTPUT" | grep "STOCK_CLASS_FACET=" | cut -d'=' -f2 | tr -d ' ') +STOCK_FACET=$(echo "$DEPLOY_OUTPUT" | grep "STOCK_FACET=" | cut -d'=' -f2 | tr -d ' ') +CONVERTIBLES_FACET=$(echo "$DEPLOY_OUTPUT" | grep "CONVERTIBLES_FACET=" | cut -d'=' -f2 | tr -d ' ') +EQUITY_COMPENSATION_FACET=$(echo "$DEPLOY_OUTPUT" | grep "EQUITY_COMPENSATION_FACET=" | cut -d'=' -f2 | tr -d ' ') +STOCK_PLAN_FACET=$(echo "$DEPLOY_OUTPUT" | grep "STOCK_PLAN_FACET=" | cut -d'=' -f2 | tr -d ' ') +WARRANT_FACET=$(echo "$DEPLOY_OUTPUT" | grep "WARRANT_FACET=" | cut -d'=' -f2 | tr -d ' ') +STAKEHOLDER_NFT_FACET=$(echo "$DEPLOY_OUTPUT" | grep "STAKEHOLDER_NFT_FACET=" | cut -d'=' -f2 | tr -d ' ') +ACCESS_CONTROL_FACET=$(echo "$DEPLOY_OUTPUT" | grep "ACCESS_CONTROL_FACET=" | cut -d'=' -f2 | tr -d ' ') + + +echo "FACTORY_ADDRESS: $FACTORY_ADDRESS" +echo "REFERENCE_DIAMOND: $REFERENCE_DIAMOND" +echo "DIAMOND_LOUPE_FACET: $DIAMOND_LOUPE_FACET" +echo "ISSUER_FACET: $ISSUER_FACET" +echo "STAKEHOLDER_FACET: $STAKEHOLDER_FACET" +echo "STOCK_CLASS_FACET: $STOCK_CLASS_FACET" +echo "STOCK_FACET: $STOCK_FACET" +echo "CONVERTIBLES_FACET: $CONVERTIBLES_FACET" +echo "EQUITY_COMPENSATION_FACET: $EQUITY_COMPENSATION_FACET" +echo "STOCK_PLAN_FACET: $STOCK_PLAN_FACET" +echo "WARRANT_FACET: $WARRANT_FACET" +echo "STAKEHOLDER_NFT_FACET: $STAKEHOLDER_NFT_FACET" +echo "ACCESS_CONTROL_FACET: $ACCESS_CONTROL_FACET" + +# Only attempt verification for non-local environments +if [ "$ENVIRONMENT" != "local" ]; then + echo "Waiting for deployment to be confirmed..." + sleep 30 + + echo "Verifying contracts..." + # Verify Factory + forge verify-contract $FACTORY_ADDRESS src/core/CapTableFactory.sol:CapTableFactory \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY \ + --verifier-url https://api-sepolia.basescan.org/api \ + --constructor-args $(cast abi-encode "constructor(address)" $REFERENCE_DIAMOND) + + # Verify Diamond + forge verify-contract $REFERENCE_DIAMOND src/core/CapTable.sol:CapTable \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY \ + --constructor-args $(cast abi-encode "constructor(address)" $FACTORY_ADDRESS) + + # Verify Diamond Loupe Facet + forge verify-contract $DIAMOND_LOUPE_FACET src/facets/DiamondLoupeFacet.sol:DiamondLoupeFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Issuer Facet + forge verify-contract $ISSUER_FACET src/facets/IssuerFacet.sol:IssuerFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Stakeholder Facet + forge verify-contract $STAKEHOLDER_FACET src/facets/StakeholderFacet.sol:StakeholderFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Stock Class Facet + forge verify-contract $STOCK_CLASS_FACET src/facets/StockClassFacet.sol:StockClassFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Stock Facet + forge verify-contract $STOCK_FACET src/facets/StockFacet.sol:StockFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Convertibles Facet + forge verify-contract $CONVERTIBLES_FACET src/facets/ConvertiblesFacet.sol:ConvertiblesFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Equity Compensation Facet + forge verify-contract $EQUITY_COMPENSATION_FACET src/facets/EquityCompensationFacet.sol:EquityCompensationFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Stock Plan Facet + forge verify-contract $STOCK_PLAN_FACET src/facets/StockPlanFacet.sol:StockPlanFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Warrant Facet + forge verify-contract $WARRANT_FACET src/facets/WarrantFacet.sol:WarrantFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Stakeholder NFT Facet + forge verify-contract $STAKEHOLDER_NFT_FACET src/facets/StakeholderNFTFacet.sol:StakeholderNFTFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Access Control Facet + forge verify-contract $ACCESS_CONTROL_FACET src/facets/AccessControlFacet.sol:AccessControlFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + echo "Verification complete!" +fi diff --git a/scripts/docker_container_utils.sh b/scripts/docker_container_utils.sh new file mode 100644 index 00000000..cc26a166 --- /dev/null +++ b/scripts/docker_container_utils.sh @@ -0,0 +1,102 @@ +#!/bin/bash + +# Function to check container health +check_health() { + local container_name="$1" + docker inspect --format='{{.State.Health.Status}}' "$container_name" 2>/dev/null || \ + docker inspect --format='{{.State.Status}}' "$container_name" 2>/dev/null || \ + echo "unknown" +} + +# Function to wait for container to be healthy +wait_for_health() { + local container_name="$1" + local timeout_duration=30 + local start_time=$(date +%s) + local end_time=$((start_time + timeout_duration)) + + echo "Waiting for container to be healthy..." + while (( $(date +%s) < end_time )); do + local status=$(check_health "$container_name") + echo "Container health status: [$status]" + + if [[ "$status" = "healthy" ]]; then + return 0 + fi + sleep 2 + done + + return 1 +} + +# Function to handle container switch and cleanup +handle_container_switch() { + local container_name="$1" + local deploy_time="$2" + local environment="$3" + + echo 'New container is healthy, switching traffic...' + # Get current container config + local config=$(docker inspect "$container_name" --format='{{range .Config.Env}} -e {{.}}{{end}}') + local image=$(docker inspect "$container_name" --format='{{.Config.Image}}') + + # Create new container but don't start it yet + echo "Creating final container..." + docker create \ + --name ocp-${environment}-final \ + -p 8080:8080 \ + --health-cmd='curl -f http://localhost:8080/health || exit 1' \ + --health-interval='2s' \ + --health-retries='3' \ + --health-timeout='5s' \ + --restart always \ + $config \ + -v '/home/ubuntu/global-bundle.pem:/global-bundle.pem' \ + $image + + # Atomic switch: stop old container and start new one as quickly as possible + echo "Performing atomic switch..." + ( + # Use subshell for atomic operation + docker stop ocp-${environment} 2>/dev/null + docker rm ocp-${environment} 2>/dev/null + docker rename ocp-${environment}-final ocp-${environment} + docker start ocp-${environment} + ) & # Run in background + + # Wait for background process + wait $! + + # Verify new container is running + if ! docker ps --filter "name=ocp-${environment}" --filter "status=running" | grep -q ocp-${environment}; then + echo "Switch failed, rolling back..." + return 1 + fi + + + # Stop and remove the old container + docker stop "$container_name" + docker rm "$container_name" + + echo 'Performing final cleanup...' + docker image ls "ocp-${environment}:*" --format '{{.ID}}' | tail -n +3 | xargs -r docker image rm + docker system prune -af --volumes + docker tag "ocp-${environment}:${deploy_time}" ocp-${environment}:latest + + echo 'Deployment successful!' + cd /home/ubuntu && rm -rf "app-${deploy_time}" + return 0 +} + +# Function to handle failed deployment +handle_failed_deployment() { + local container_name="$1" + local deploy_time="$2" + local environment="$3" + echo 'New container failed health check, rolling back...' + docker stop "$container_name" + docker rm "$container_name" + docker image rm "ocp-${environment}:${deploy_time}" + cd /home/ubuntu && rm -rf "app-${deploy_time}" + return 1 +} \ No newline at end of file diff --git a/scripts/sync.sh b/scripts/sync.sh new file mode 100755 index 00000000..940f0473 --- /dev/null +++ b/scripts/sync.sh @@ -0,0 +1,140 @@ +#!/bin/bash + +# Sets default environment to "local" if no environment is specified +ENVIRONMENT="local" + +# Function to cleanup processes on exit +cleanup() { + # Kill anvil if we started it + if [ ! -z "$ANVIL_PID" ]; then + echo "Stopping anvil..." + kill $ANVIL_PID + fi + # Remove temp env file if it exists + if [ -f "$TEMP" ]; then + rm -f "$TEMP" + fi +} + +# Set single trap for cleanup +trap cleanup EXIT INT TERM + +# Processes command line arguments +while [[ "$#" -gt 0 ]]; do + case $1 in + --env=*) ENVIRONMENT="${1#*=}" ;; + *) echo "Unknown parameter: $1"; exit 1 ;; + esac + shift +done + + +# Constructs env file path based on environment +# Example: .env.local, .env.dev, .env.prod +USE_ENV_FILE=".env.$ENVIRONMENT" + +# Exits if the environment file doesn't exist +[ ! -f "$USE_ENV_FILE" ] && echo "Error: $USE_ENV_FILE does not exist" && exit 1 + +# Loads environment variables from the env file +set -a +source "$USE_ENV_FILE" +set +a + +# Check and start anvil if not running +if ! nc -z localhost 8546 2>/dev/null; then + echo "Starting anvil..." + anvil --port 8546 > /dev/null 2>&1 & + ANVIL_PID=$! + + # Wait for anvil to start + echo "Waiting for anvil to start..." + until nc -z localhost 8546 2>/dev/null; do + sleep 1 + done + echo "✅ Anvil started on port 8546" + + # Set LOCAL_RPC for the script + export LOCAL_RPC="http://localhost:8546" +fi + +# Creates a temporary copy of env file in the chain directory +TEMP=$PWD/chain/.env.temp +cp "$USE_ENV_FILE" "$TEMP" + +# Validate required environment variables +if [ -z "$REFERENCE_DIAMOND" ]; then + echo "Error: REFERENCE_DIAMOND is not set in $USE_ENV_FILE" + exit 1 +fi + +# Validate required environment variables +if [ -z "$FACTORY_ADDRESS" ]; then + echo "Error: FACTORY_ADDRESS is not set in $USE_ENV_FILE" + exit 1 +fi + +# Add confirmation step for non-local environments +if [ "$ENVIRONMENT" != "local" ]; then + echo "⚠️ You are about to sync contracts in $ENVIRONMENT environment" + echo "RPC URL: $RPC_URL" + echo "Reference Diamond: $REFERENCE_DIAMOND" + read -p "Are you sure you want to continue? (y/N) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Sync cancelled" + exit 1 + fi +fi + +cd chain + +echo "🔄 Starting sync process..." + +# Step 1: Run SyncFacets script +echo "🔄 Syncing facets..." +echo "LOCAL_RPC: $LOCAL_RPC" +echo "REMOTE_RPC: $RPC_URL" +LOCAL_RPC=${LOCAL_RPC:-"http://localhost:8546"} REMOTE_RPC=$RPC_URL forge script script/SyncFacets.s.sol \ + --broadcast \ + --rpc-url $RPC_URL \ + --private-key $PRIVATE_KEY \ + -vvvv + +if [ $? -ne 0 ]; then + echo "❌ SyncFacets script failed" + exit 1 +fi + +# Add confirmation step for non-local environments +if [ "$ENVIRONMENT" != "local" ]; then + echo "⚠️ You are about to sync Diamonds in $ENVIRONMENT environment" + echo "RPC URL: $RPC_URL" + echo "Reference Diamond: $REFERENCE_DIAMOND" + echo "Factory Address: $FACTORY_ADDRESS" + read -p "Are you sure you want to continue? (y/N) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Sync cancelled" + exit 1 + fi +fi + +# Step 2: Run SyncDiamonds script +# echo "🔄 Syncing deployed diamonds..." +# echo "Using Factory Address: $FACTORY_ADDRESS" +# echo "Using Reference Diamond: $REFERENCE_DIAMOND" + +# Run forge with verbose output and stream logs +# forge script script/SyncDiamonds.s.sol \ +# --broadcast \ +# --rpc-url $RPC_URL \ +# --private-key $PRIVATE_KEY \ +# -vvvv + +# if [ $? -ne 0 ]; then +# echo "❌ SyncDiamonds script failed" +# exit 1 +# fi + +echo "✅ Sync completed successfully!" diff --git a/scripts/verify_contracts.sh b/scripts/verify_contracts.sh new file mode 100755 index 00000000..1194dc47 --- /dev/null +++ b/scripts/verify_contracts.sh @@ -0,0 +1,174 @@ +#!/bin/bash + +# Sets default environment to "local" if no environment is specified +ENVIRONMENT="local" + +# Processes command line arguments +# Example: ./deploy_factory.local.sh --env=dev +while [[ "$#" -gt 0 ]]; do + case $1 in + --env=*) ENVIRONMENT="${1#*=}" ;; # Extracts value after --env= + *) echo "Unknown parameter: $1"; exit 1 ;; + esac + shift +done + + +# Constructs env file path based on environment +# Example: .env.local, .env.dev, .env.prod +USE_ENV_FILE=".env.$ENVIRONMENT" + +# Exits if the environment file doesn't exist +[ ! -f "$USE_ENV_FILE" ] && echo "Error: $USE_ENV_FILE does not exist" && exit 1 + +# Loads environment variables from the env file +# set -a: automatically exports all variables +# source: loads the env file +# set +a: stops auto-exporting +set -a +source "$USE_ENV_FILE" +set +a + +# Creates a temporary copy of env file in the chain directory +# TEMP will be something like /your/path/chain/.env +TEMP=$PWD/chain/.env +cp "$USE_ENV_FILE" "$TEMP" +# Removes the temporary file when script exits +trap "rm $TEMP" EXIT + +# Add confirmation step for non-local environments +if [ "$ENVIRONMENT" != "local" ]; then + echo "⚠️ You are about to deploy to $ENVIRONMENT environment" + echo "RPC URL will be: $RPC_URL" + read -p "Are you sure you want to continue? (y/N) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Deployment cancelled" + exit 1 + fi +fi + +# Changes to chain directory and runs the forge deploy script +cd chain +echo $RPC_URL +echo $CHAIN_ID + +# Extract all addresses +# FACTORY_ADDRESS=$(echo "$DEPLOY_OUTPUT" | grep "FACTORY_ADDRESS=" | cut -d'=' -f2 | tr -d ' ') +# REFERENCE_DIAMOND=$(echo "$DEPLOY_OUTPUT" | grep "REFERENCE_DIAMOND=" | cut -d'=' -f2 | tr -d ' ') +# DIAMOND_LOUPE_FACET=$(echo "$DEPLOY_OUTPUT" | grep "DIAMOND_LOUPE_FACET=" | cut -d'=' -f2 | tr -d ' ') +# ISSUER_FACET=$(echo "$DEPLOY_OUTPUT" | grep "ISSUER_FACET=" | cut -d'=' -f2 | tr -d ' ') +# STAKEHOLDER_FACET=$(echo "$DEPLOY_OUTPUT" | grep "STAKEHOLDER_FACET=" | cut -d'=' -f2 | tr -d ' ') +# STOCK_CLASS_FACET=$(echo "$DEPLOY_OUTPUT" | grep "STOCK_CLASS_FACET=" | cut -d'=' -f2 | tr -d ' ') +# STOCK_FACET=$(echo "$DEPLOY_OUTPUT" | grep "STOCK_FACET=" | cut -d'=' -f2 | tr -d ' ') +# CONVERTIBLES_FACET=$(echo "$DEPLOY_OUTPUT" | grep "CONVERTIBLES_FACET=" | cut -d'=' -f2 | tr -d ' ') +# EQUITY_COMPENSATION_FACET=$(echo "$DEPLOY_OUTPUT" | grep "EQUITY_COMPENSATION_FACET=" | cut -d'=' -f2 | tr -d ' ') +# STOCK_PLAN_FACET=$(echo "$DEPLOY_OUTPUT" | grep "STOCK_PLAN_FACET=" | cut -d'=' -f2 | tr -d ' ') +# WARRANT_FACET=$(echo "$DEPLOY_OUTPUT" | grep "WARRANT_FACET=" | cut -d'=' -f2 | tr -d ' ') +# STAKEHOLDER_NFT_FACET=$(echo "$DEPLOY_OUTPUT" | grep "STAKEHOLDER_NFT_FACET=" | cut -d'=' -f2 | tr -d ' ') +# ACCESS_CONTROL_FACET=$(echo "$DEPLOY_OUTPUT" | grep "ACCESS_CONTROL_FACET=" | cut -d'=' -f2 | tr -d ' ') + +FACTORY_ADDRESS=0x8BD1b1b01F10aa23715951E556a10e16D8fbeBF6 +REFERENCE_DIAMOND=0xe213A65245deFeDC063adF3bbC1a93F7DCf3389b +DIAMOND_LOUPE_FACET=0x97bEC0687B73DA89c9584da4b977CcC30919FB3D +ISSUER_FACET=0x6C802F7Ca87F2B0008EEe7281D53a9f562112397 +STAKEHOLDER_FACET=0xDACffcA48c705e34e6742195BA09407df03274C3 +STOCK_CLASS_FACET=0x6b2a592FA5416Ce9Dc47A704627327A4FA70b6d3 +STOCK_FACET=0x67312EeB97625BEb5c3f95E749D18E8DCD2575Ab +CONVERTIBLES_FACET=0x80263F0c2a7Eaf6bF9CeF082Ec30FA3AbE5Dc7fC +EQUITY_COMPENSATION_FACET=0x1A8707897021d64B3d7F03301B22c15995E14Bd8 +STOCK_PLAN_FACET=0x22D42425Db6ce12c99080d0012fff86d19AAb765 +WARRANT_FACET=0x157e62Bd607f8bF95623007b67Eea4540c7F0adF +STAKEHOLDER_NFT_FACET=0x4Da7475D2ef43B6a5AE60Dade686487bEdA91104 +ACCESS_CONTROL_FACET=0xbBA2F4592f1647193f52f743eE28b8cE286783Cb + + +echo "FACTORY_ADDRESS: $FACTORY_ADDRESS" +echo "REFERENCE_DIAMOND: $REFERENCE_DIAMOND" +echo "DIAMOND_LOUPE_FACET: $DIAMOND_LOUPE_FACET" +echo "ISSUER_FACET: $ISSUER_FACET" +echo "STAKEHOLDER_FACET: $STAKEHOLDER_FACET" +echo "STOCK_CLASS_FACET: $STOCK_CLASS_FACET" +echo "STOCK_FACET: $STOCK_FACET" +echo "CONVERTIBLES_FACET: $CONVERTIBLES_FACET" +echo "EQUITY_COMPENSATION_FACET: $EQUITY_COMPENSATION_FACET" +echo "STOCK_PLAN_FACET: $STOCK_PLAN_FACET" +echo "WARRANT_FACET: $WARRANT_FACET" +echo "STAKEHOLDER_NFT_FACET: $STAKEHOLDER_NFT_FACET" +echo "ACCESS_CONTROL_FACET: $ACCESS_CONTROL_FACET" + +# Only attempt verification for non-local environments +if [ "$ENVIRONMENT" != "local" ]; then + echo "Waiting for deployment to be confirmed..." + sleep 30 + + echo "Verifying contracts..." + # Verify Factory + forge verify-contract $FACTORY_ADDRESS src/core/CapTableFactory.sol:CapTableFactory \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY \ + --constructor-args $(cast abi-encode "constructor(address)" $REFERENCE_DIAMOND) + + # Verify Diamond + forge verify-contract $REFERENCE_DIAMOND src/core/CapTable.sol:CapTable \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY \ + --constructor-args $(cast abi-encode "constructor(address)" $FACTORY_ADDRESS) + + # Verify Diamond Loupe Facet + forge verify-contract $DIAMOND_LOUPE_FACET src/facets/DiamondLoupeFacet.sol:DiamondLoupeFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Issuer Facet + forge verify-contract $ISSUER_FACET src/facets/IssuerFacet.sol:IssuerFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Stakeholder Facet + forge verify-contract $STAKEHOLDER_FACET src/facets/StakeholderFacet.sol:StakeholderFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Stock Class Facet + forge verify-contract $STOCK_CLASS_FACET src/facets/StockClassFacet.sol:StockClassFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Stock Facet + forge verify-contract $STOCK_FACET src/facets/StockFacet.sol:StockFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Convertibles Facet + forge verify-contract $CONVERTIBLES_FACET src/facets/ConvertiblesFacet.sol:ConvertiblesFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Equity Compensation Facet + forge verify-contract $EQUITY_COMPENSATION_FACET src/facets/EquityCompensationFacet.sol:EquityCompensationFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Stock Plan Facet + forge verify-contract $STOCK_PLAN_FACET src/facets/StockPlanFacet.sol:StockPlanFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Warrant Facet + forge verify-contract $WARRANT_FACET src/facets/WarrantFacet.sol:WarrantFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Stakeholder NFT Facet + forge verify-contract $STAKEHOLDER_NFT_FACET src/facets/StakeholderNFTFacet.sol:StakeholderNFTFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + # Verify Access Control Facet + forge verify-contract $ACCESS_CONTROL_FACET src/facets/AccessControlFacet.sol:AccessControlFacet \ + --chain-id $CHAIN_ID \ + --etherscan-api-key $ETHERSCAN_API_KEY + + echo "Verification complete!" +fi diff --git a/forge_install.sh b/setup.sh similarity index 92% rename from forge_install.sh rename to setup.sh index 5c95a1d0..7beaa39e 100755 --- a/forge_install.sh +++ b/setup.sh @@ -27,8 +27,9 @@ forge remappings > remappings.txt || exit 1 echo '@facets/=src/facets/' >> remappings.txt echo '@libraries/=src/libraries/' >> remappings.txt echo '@core/=src/core/' >> remappings.txt +echo '@interfaces/=src/interfaces/' >> remappings.txt echo "Building contracts..." -forge build --via-ir || exit 1 +forge build || exit 1 echo "Setup completed successfully!" \ No newline at end of file diff --git a/src/app.js b/src/app.js index b82c798b..3771ecc7 100644 --- a/src/app.js +++ b/src/app.js @@ -6,14 +6,13 @@ import { setTag } from "@sentry/node"; import * as Sentry from "@sentry/node"; // Routes -import historicalTransactions from "./routes/historicalTransactions.js"; import mainRoutes from "./routes/index.js"; import issuerRoutes from "./routes/issuer.js"; -import stakeholderRoutes from "./routes/stakeholder.js"; +import stakeholderRoutes from "./routes/stakeholder/index.js"; import stockClassRoutes from "./routes/stockClass.js"; import stockLegendRoutes from "./routes/stockLegend.js"; import stockPlanRoutes from "./routes/stockPlan.js"; -import transactionRoutes from "./routes/transactions.js"; +import transactionRoutes from "./routes/transactions/index.js"; import valuationRoutes from "./routes/valuation.js"; import vestingTermsRoutes from "./routes/vestingTerms.js"; import statsRoutes from "./routes/stats/index.js"; @@ -23,6 +22,7 @@ import ocfRoutes from "./routes/ocf.js"; import { readAllIssuers, readIssuerById } from "./db/operations/read.js"; import { contractCache } from "./utils/simple_caches.js"; import { getContractInstance } from "./chain-operations/getContractInstances.js"; +import { getChainConfig, SUPPORTED_CHAINS } from "./utils/chains.js"; setupEnv(); Sentry.init({ @@ -35,17 +35,29 @@ Sentry.init({ const app = express(); const PORT = process.env.PORT; -const CHAIN = process.env.CHAIN; // Middlewares const chainMiddleware = (req, res, next) => { - req.chain = CHAIN; + // For issuer creation, expect chainId in the request + const chainId = req.body.chain_id; + if (!chainId) { + return res.status(400).send("chain_id is required for issuer creation"); + } + + // Validate that this is a supported chain + const chainConfig = getChainConfig(Number(chainId)); + if (!chainConfig) { + return res.status(400).send(`Unsupported chain ID: ${chainId}. Supported chains are: ${Object.keys(SUPPORTED_CHAINS).join(", ")}`); + } + + req.chain = Number(chainId); next(); }; // Middleware to get or create contract instance // the listener is first started on deployment, then here as a backup const contractMiddleware = async (req, res, next) => { + // Log route information if (!req.body.issuerId) { console.log("❌ | No issuer ID"); return res.status(400).send("issuerId is required"); @@ -56,13 +68,14 @@ const contractMiddleware = async (req, res, next) => { if (!issuer || !issuer.id) return res.status(404).send("issuer not found "); // Check if contract instance already exists in cache - if (!contractCache[req.body.issuerId]) { - const contract = await getContractInstance(issuer.deployed_to); - contractCache[req.body.issuerId] = { contract }; + const cacheKey = `${issuer.chain_id}-${req.body.issuerId}`; + if (!contractCache[cacheKey]) { + const contract = await getContractInstance(issuer.deployed_to, issuer.chain_id); + contractCache[cacheKey] = { contract }; } setTag("issuerId", req.body.issuerId); - req.contract = contractCache[req.body.issuerId].contract; + req.contract = contractCache[cacheKey].contract; next(); }; @@ -70,7 +83,7 @@ app.use(urlencoded({ limit: "50mb", extended: true })); app.use(json({ limit: "50mb" })); app.enable("trust proxy"); -app.use("/", chainMiddleware, mainRoutes); +app.use("/", mainRoutes); app.use("/issuer", chainMiddleware, issuerRoutes); app.use("/stakeholder", contractMiddleware, stakeholderRoutes); app.use("/stock-class", contractMiddleware, stockClassRoutes); @@ -80,7 +93,6 @@ app.use("/stock-legend", stockLegendRoutes); app.use("/stock-plan", contractMiddleware, stockPlanRoutes); app.use("/valuation", valuationRoutes); app.use("/vesting-terms", vestingTermsRoutes); -app.use("/historical-transactions", historicalTransactions); app.use("/stats", statsRoutes); app.use("/export", exportRoutes); app.use("/ocf", ocfRoutes); @@ -99,20 +111,32 @@ const startServer = async () => { const issuers = (await readAllIssuers()) || null; if (issuers) { - const contractAddresses = issuers - .filter((issuer) => issuer?.deployed_to) - .reduce((acc, issuer) => { - acc[issuer.id] = issuer.deployed_to; - return acc; - }, {}); - - console.log(contractAddresses); - console.log("Issuer -> Contract Address"); - const contractsToWatch = Object.values(contractAddresses); - console.log("Watching ", contractsToWatch.length, " Contracts"); - startListener(contractsToWatch); + // Group contracts by chain ID + const contractsToWatch = issuers + .filter((issuer) => issuer?.deployed_to && issuer?.chain_id) + .map((issuer) => ({ + address: issuer.deployed_to, + chain_id: issuer.chain_id, + name: issuer.legal_name, + })); + + console.log("Watching contracts by chain:"); + const contractsByChain = contractsToWatch.reduce((acc, contract) => { + acc[contract.chain_id] = (acc[contract.chain_id] || 0) + 1; + return acc; + }, {}); + Object.entries(contractsToWatch).forEach(([_ /*id*/, data]) => { + console.log(`${data.name.padEnd(32)} -> ${data.address}`); + }); + + Object.entries(contractsByChain).forEach(([chainId, count]) => { + console.log(`Chain ${chainId}: ${count} contracts`); + }); + + await startListener(contractsToWatch); } }); + app.on("error", (err) => { console.error(err); if (err.code === "EADDRINUSE") { diff --git a/src/chain-operations/deployCapTable.js b/src/chain-operations/deployCapTable.js index 37b1176e..0e9f06d2 100644 --- a/src/chain-operations/deployCapTable.js +++ b/src/chain-operations/deployCapTable.js @@ -1,5 +1,5 @@ import { ethers } from "ethers"; -import CAP_TABLE_FACTORY from "../../chain/out/DiamondCapTableFactory.sol/DiamondCapTableFactory.json"; +import CAP_TABLE_FACTORY from "../../chain/out/CapTableFactory.sol/CapTableFactory.json"; import STAKEHOLDER_FACET from "../../chain/out/StakeholderFacet.sol/StakeholderFacet.json"; import ISSUER_FACET from "../../chain/out/IssuerFacet.sol/IssuerFacet.json"; import STOCK_CLASS_FACET from "../../chain/out/StockClassFacet.sol/StockClassFacet.json"; @@ -9,11 +9,13 @@ import WARRANT_FACET from "../../chain/out/WarrantFacet.sol/WarrantFacet.json"; import EQUITY_COMPENSATION_FACET from "../../chain/out/EquityCompensationFacet.sol/EquityCompensationFacet.json"; import STOCK_PLAN_FACET from "../../chain/out/StockPlanFacet.sol/StockPlanFacet.json"; import STAKEHOLDER_NFT_FACET from "../../chain/out/StakeholderNFTFacet.sol/StakeholderNFTFacet.json"; +import ACCESS_CONTROL_FACET from "../../chain/out/AccessControlFacet.sol/AccessControlFacet.json"; import { toScaledBigNumber } from "../utils/convertToFixedPointDecimals.js"; import { setupEnv } from "../utils/env.js"; import getProvider from "./getProvider.js"; import { findOne } from "../db/operations/atomic"; import Factory from "../db/objects/Factory.js"; +import assert from "node:assert"; setupEnv(); @@ -27,20 +29,30 @@ export const facetsABI = [ ...WARRANT_FACET.abi, ...EQUITY_COMPENSATION_FACET.abi, ...STAKEHOLDER_NFT_FACET.abi, + ...ACCESS_CONTROL_FACET.abi, ]; const WALLET_PRIVATE_KEY = process.env.PRIVATE_KEY; -const provider = getProvider(); -export const wallet = new ethers.Wallet(WALLET_PRIVATE_KEY, provider); -async function deployCapTable(issuerId, initial_shares_authorized) { +export const getWallet = async (chainId) => { + assert(WALLET_PRIVATE_KEY, "WALLET_PRIVATE_KEY is not set"); + assert(chainId, "chainId is not set"); + + const provider = getProvider(chainId); + return new ethers.Wallet(WALLET_PRIVATE_KEY, provider); +}; + +async function deployCapTable(issuerId, initial_shares_authorized, chainId) { + // Get provider for specified chain + const wallet = await getWallet(chainId); console.log("🗽 | Wallet address: ", wallet.address); - const factory = await findOne(Factory, { version: "DIAMOND" }); + // Find factory for this chain + const factory = await findOne(Factory, { version: "DIAMOND", chain_id: chainId }); const factoryAddress = factory?.factory_address; if (!factoryAddress) { - throw new Error(`❌ | Factory address not found`); + throw new Error(`Factory not found for chain ${chainId}`); } console.log("🏭 | Factory address: ", factoryAddress); @@ -48,21 +60,20 @@ async function deployCapTable(issuerId, initial_shares_authorized) { console.log("Creating a new cap table..."); const tx = await capTableFactory.createCapTable(issuerId, toScaledBigNumber(initial_shares_authorized)); - await tx.wait(); + const receipt = await tx.wait(); console.log("Cap table created"); const capTableCount = await capTableFactory.getCapTableCount(); console.log("📄 | Cap table count: ", capTableCount); - const diamondAddress = await capTableFactory.capTables(capTableCount - BigInt(1)); - console.log("✅ | Diamond address: ", diamondAddress); - - // Diamond Facets ABI + const captableAddress = await capTableFactory.capTables(capTableCount - BigInt(1)); + console.log("✅ | Cap table address: ", captableAddress); return { - contract: new ethers.Contract(diamondAddress, facetsABI, wallet), - address: diamondAddress, - deployHash: tx.hash, + contract: new ethers.Contract(captableAddress, facetsABI, wallet), + address: captableAddress, + deployHash: receipt.hash, + receipt, }; } diff --git a/src/chain-operations/getContractInstances.js b/src/chain-operations/getContractInstances.js index 5ff769b7..a4afc68a 100644 --- a/src/chain-operations/getContractInstances.js +++ b/src/chain-operations/getContractInstances.js @@ -1,5 +1,5 @@ import { ethers } from "ethers"; -import CAP_TABLE_FACTORY from "../../chain/out/DiamondCapTableFactory.sol/DiamondCapTableFactory.json"; +import CAP_TABLE_FACTORY from "../../chain/out/CapTableFactory.sol/CapTableFactory.json"; import { setupEnv } from "../utils/env.js"; import getProvider from "./getProvider.js"; import STAKEHOLDER_FACET from "../../chain/out/StakeholderFacet.sol/StakeholderFacet.json"; @@ -14,8 +14,7 @@ import STAKEHOLDER_NFT_FACET from "../../chain/out/StakeholderNFTFacet.sol/Stake setupEnv(); -const provider = getProvider(); -export const getContractInstance = (address) => { +export const getContractInstance = (address, chainId) => { const WALLET_PRIVATE_KEY = process.env.PRIVATE_KEY; // Create a combined ABI from all facets const combinedABI = [ @@ -31,6 +30,7 @@ export const getContractInstance = (address) => { ...STAKEHOLDER_NFT_FACET.abi, ]; + const provider = getProvider(chainId); const wallet = new ethers.Wallet(WALLET_PRIVATE_KEY, provider); const contract = new ethers.Contract(address, combinedABI, wallet); diff --git a/src/chain-operations/getProvider.js b/src/chain-operations/getProvider.js index c5e03b83..d44d30c0 100644 --- a/src/chain-operations/getProvider.js +++ b/src/chain-operations/getProvider.js @@ -1,39 +1,13 @@ import { ethers } from "ethers"; -import { setupEnv } from "../utils/env"; +import { getChainConfig } from "../utils/chains.js"; -setupEnv(); - -let RPC_URL = process.env.RPC_URL; -const CHAIN_ID = process.env.CHAIN_ID; - -const LOCAL_RPC = "ws://127.0.0.1:8545"; - -// make sure it's websocket url wss -RPC_URL = RPC_URL.replace("http", "ws"); -console.log("RPC_URL", RPC_URL); -let provider = null; - -const getProvider = () => { - if (provider) { - console.log("🔗 | Using existing provider"); - return provider; +function getProvider(chainId) { + const chainConfig = getChainConfig(chainId); + if (!chainConfig) { + throw new Error(`Unsupported chain ID: ${chainId}`); } - if (RPC_URL === LOCAL_RPC) { - console.log("🔗 | Connecting to local network: ", RPC_URL); - const customNetwork = { - chainId: parseInt(CHAIN_ID), - name: "local", - }; - provider = new ethers.JsonRpcProvider(RPC_URL.replace("ws", "http"), customNetwork); - console.log("🔗 | Connected to local network: ", RPC_URL); - } else { - console.log("🔗 | Connecting to network: ", RPC_URL); - // provider = new ethers.WebSocketProvider(RPC_URL); - provider = new ethers.JsonRpcProvider(RPC_URL.replace("ws", "http")); - console.log("🔗 | Connected to network: ", RPC_URL); - } - return provider; -}; + return new ethers.JsonRpcProvider(chainConfig.rpcUrl); +} export default getProvider; diff --git a/src/chain-operations/structs.js b/src/chain-operations/structs.js index 7b7c6436..71991731 100644 --- a/src/chain-operations/structs.js +++ b/src/chain-operations/structs.js @@ -1,155 +1,16 @@ -export const StockIssuance = { - type: "tuple", - baseType: "tuple", - components: [ - { type: "bytes16", baseType: "bytes16", name: "stock_class_id" }, - { type: "uint256", baseType: "uint256", name: "share_price" }, - { type: "uint256", baseType: "uint256", name: "quantity" }, - { type: "bytes16", baseType: "bytes16", name: "stakeholder_id" }, - { type: "bytes16", baseType: "bytes16", name: "security_id" }, - ], -}; - -export const StockTransfer = { - type: "tuple", - baseType: "tuple", - components: [ - { type: "bytes16", baseType: "bytes16", name: "id" }, - { type: "string", baseType: "string", name: "object_type" }, - { type: "uint256", baseType: "uint256", name: "quantity" }, - { - type: "string[]", - baseType: "array", - arrayLength: -1, - arrayChildren: { type: "string", baseType: "string" }, - name: "comments", - }, - { type: "bytes16", baseType: "bytes16", name: "security_id" }, - { type: "string", baseType: "string", name: "consideration_text" }, - { type: "bytes16", baseType: "bytes16", name: "balance_security_id" }, - { - type: "bytes16[]", - baseType: "array", - arrayLength: -1, - arrayChildren: { type: "bytes16", baseType: "bytes16" }, - name: "resulting_security_ids", - }, - ], -}; - -export const StockCancellation = { - type: "tuple", - baseType: "tuple", - components: [ - { type: "bytes16", baseType: "bytes16", name: "id" }, - { type: "string", baseType: "string", name: "object_type" }, - { type: "uint256", baseType: "uint256", name: "quantity" }, - { - type: "string[]", - baseType: "array", - arrayLength: -1, - arrayChildren: { type: "string", baseType: "string" }, - name: "comments", - }, - { type: "bytes16", baseType: "bytes16", name: "security_id" }, - { type: "string", baseType: "string", name: "reason_text" }, - { type: "bytes16", baseType: "bytes16", name: "balance_security_id" }, - ], -}; - -export const StockRetraction = { - type: "tuple", - baseType: "tuple", - components: [ - { type: "bytes16", baseType: "bytes16", name: "id" }, - { type: "string", baseType: "string", name: "object_type" }, - { - type: "string[]", - baseType: "array", - arrayLength: -1, - arrayChildren: { type: "string", baseType: "string" }, - name: "comments", - }, - { type: "bytes16", baseType: "bytes16", name: "security_id" }, - { type: "string", baseType: "string", name: "reason_text" }, - ], -}; - -export const StockReissuance = { - type: "tuple", - baseType: "tuple", - components: [ - { type: "bytes16", baseType: "bytes16", name: "id" }, - { type: "string", baseType: "string", name: "object_type" }, - { - type: "string[]", - baseType: "array", - arrayLength: -1, - arrayChildren: { type: "string", baseType: "string" }, - name: "comments", - }, - { type: "bytes16", baseType: "bytes16", name: "security_id" }, - { - type: "bytes16[]", - baseType: "array", - arrayLength: -1, - arrayChildren: { type: "bytes16", baseType: "bytes16" }, - name: "resulting_security_ids", - }, - { type: "bytes16", baseType: "bytes16", name: "split_transaction_id" }, - { type: "string", baseType: "string", name: "reason_text" }, - ], -}; +import ISTOCK_FACET from "../../chain/out/IStockFacet.sol/IStockFacet.json"; +import ICONVERTIBLES_FACET from "../../chain/out/IConvertiblesFacet.sol/IConvertiblesFacet.json"; +import IWARRANTS_FACET from "../../chain/out/IWarrantFacet.sol/IWarrantFacet.json"; +import IEQUITY_COMPENSATION_FACET from "../../chain/out/IEquityCompensationFacet.sol/IEquityCompensationFacet.json"; -export const StockRepurchase = { - type: "tuple", - baseType: "tuple", - components: [ - { type: "bytes16", baseType: "bytes16", name: "id" }, - { type: "string", baseType: "string", name: "object_type" }, - { - type: "string[]", - baseType: "array", - arrayLength: -1, - arrayChildren: { type: "string", baseType: "string" }, - name: "comments", - }, - { type: "bytes16", baseType: "bytes16", name: "security_id" }, - { type: "string", baseType: "string", name: "consideration_text" }, - { type: "bytes16", baseType: "bytes16", name: "balance_security_id" }, - { type: "uint256", baseType: "uint256", name: "quantity" }, - { type: "uint256", baseType: "uint256", name: "price" }, - ], -}; - -export const StockAcceptance = { - type: "tuple", - baseType: "tuple", - components: [ - { type: "bytes16", baseType: "bytes16", name: "stock_class_id" }, - { type: "uint256", baseType: "uint256", name: "share_price" }, - { type: "uint256", baseType: "uint256", name: "quantity" }, - { type: "bytes16", baseType: "bytes16", name: "stakeholder_id" }, - { type: "bytes16", baseType: "bytes16", name: "security_id" }, - ], -}; +export const StockIssuance = ISTOCK_FACET.abi.find((fn) => fn.name === "issueStock").inputs[0]; export const IssuerAuthorizedSharesAdjustment = { type: "tuple", baseType: "tuple", components: [ - { type: "bytes16", baseType: "bytes16", name: "id" }, - { type: "string", baseType: "string", name: "object_type" }, + { type: "bytes16", baseType: "bytes16", name: "issuer_id" }, { type: "uint256", baseType: "uint256", name: "new_shares_authorized" }, - { - type: "string[]", - baseType: "array", - arrayLength: -1, - arrayChildren: { type: "string", baseType: "string" }, - name: "comments", - }, - { type: "string", baseType: "string", name: "board_approval_date" }, - { type: "string", baseType: "string", name: "stockholder_approval_date" }, ], }; @@ -157,60 +18,51 @@ export const StockClassAuthorizedSharesAdjustment = { type: "tuple", baseType: "tuple", components: [ - { type: "bytes16", baseType: "bytes16", name: "id" }, { type: "bytes16", baseType: "bytes16", name: "stock_class_id" }, - { type: "string", baseType: "string", name: "object_type" }, { type: "uint256", baseType: "uint256", name: "new_shares_authorized" }, - { - type: "string[]", - baseType: "array", - arrayLength: -1, - arrayChildren: { type: "string", baseType: "string" }, - name: "comments", - }, - { type: "string", baseType: "string", name: "board_approval_date" }, - { type: "string", baseType: "string", name: "stockholder_approval_date" }, ], }; -export const ConvertibleIssuance = { +export const StockPlanPoolAdjustment = { type: "tuple", baseType: "tuple", components: [ - { type: "bytes16", baseType: "bytes16", name: "stakeholder_id" }, - { type: "uint256", baseType: "uint256", name: "investment_amount" }, - { type: "bytes16", baseType: "bytes16", name: "security_id" }, + { type: "bytes16", baseType: "bytes16", name: "stock_plan_id" }, + { type: "uint256", baseType: "uint256", name: "new_shares_reserved" }, ], }; -export const WarrantIssuance = { - type: "tuple", - baseType: "tuple", - components: [ - { type: "bytes16", baseType: "bytes16", name: "stakeholder_id" }, - { type: "uint256", baseType: "uint256", name: "quantity" }, - { type: "bytes16", baseType: "bytes16", name: "security_id" }, - ], -}; +export const ConvertibleIssuance = ICONVERTIBLES_FACET.abi.find((fn) => fn.name === "issueConvertible").inputs[0]; + +export const WarrantIssuance = IWARRANTS_FACET.abi.find((fn) => fn.name === "issueWarrant").inputs[0]; + +export const EquityCompensationIssuance = IEQUITY_COMPENSATION_FACET.abi.find((fn) => fn.name === "issueEquityCompensation").inputs[0]; -export const EquityCompensationIssuance = { +export const EquityCompensationExercise = IEQUITY_COMPENSATION_FACET.abi.find((fn) => fn.name === "exerciseEquityCompensation").inputs[0]; + +export const StockTransfer = { type: "tuple", - baseType: "tuple", components: [ - { type: "bytes16", baseType: "bytes16", name: "stakeholder_id" }, - { type: "bytes16", baseType: "bytes16", name: "stock_class_id" }, - { type: "bytes16", baseType: "bytes16", name: "stock_plan_id" }, - { type: "uint256", baseType: "uint256", name: "quantity" }, - { type: "bytes16", baseType: "bytes16", name: "security_id" }, + { type: "bytes16", name: "consolidated_security_id" }, + { type: "bytes16", name: "transferee_security_id" }, + { type: "bytes16", name: "remainder_security_id" }, + { type: "uint256", name: "quantity" }, + { type: "uint256", name: "share_price" }, ], }; -export const EquityCompensationExercise = { +/* TODO: IMPLEMENT THIS */ +export const StockRepurchase = {}; +export const StockAcceptance = {}; +export const StockCancellation = {}; +export const StockRetraction = {}; +export const StockReissuance = {}; +// export const StockConsolidation = ISTOCK_FACET.abi.find((fn) => fn.name === "consolidateStock").inputs[0]; + +export const StockConsolidation = { type: "tuple", - baseType: "tuple", components: [ - { type: "bytes16", baseType: "bytes16", name: "equity_comp_security_id" }, - { type: "bytes16", baseType: "bytes16", name: "resulting_stock_security_id" }, - { type: "uint256", baseType: "uint256", name: "quantity" }, + { type: "bytes16[]", name: "security_ids" }, + { type: "bytes16", name: "resulting_security_id" }, ], }; diff --git a/src/chain-operations/topics.js b/src/chain-operations/topics.js new file mode 100644 index 00000000..ce629b52 --- /dev/null +++ b/src/chain-operations/topics.js @@ -0,0 +1,20 @@ +import * as ethers from "ethers"; +import ISTOCK_CLASS_FACET from "../../chain/out/IStockClassFacet.sol/IStockClassFacet.json"; +import ISTAKEHOLDER_FACET from "../../chain/out/IStakeholderFacet.sol/IStakeholderFacet.json"; +import ISTOCK_PLAN_FACET from "../../chain/out/IStockPlanFacet.sol/IStockPlanFacet.json"; +import TX_HELPER from "../../chain/out/TxHelper.sol/TxHelper.json"; + +// Helper to generate event signature from ABI event +const getEventSignature = (abi, eventName) => { + const event = abi.find((fn) => fn.name === eventName && fn.type === "event"); + if (!event) { + throw new Error(`Event ${eventName} not found in ABI`); + } + return `${event.name}(${event.inputs.map((input) => input.type).join(",")})`; +}; + +// Generate event topics from ABIs +export const TxCreated = ethers.id(getEventSignature(TX_HELPER.abi, "TxCreated")); +export const StakeholderCreated = ethers.id(getEventSignature(ISTAKEHOLDER_FACET.abi, "StakeholderCreated")); +export const StockPlanCreated = ethers.id(getEventSignature(ISTOCK_PLAN_FACET.abi, "StockPlanCreated")); +export const StockClassCreated = ethers.id(getEventSignature(ISTOCK_CLASS_FACET.abi, "StockClassCreated")); diff --git a/src/chain-operations/transactionHandlers.js b/src/chain-operations/transactionHandlers.js index b03975fe..a098a405 100644 --- a/src/chain-operations/transactionHandlers.js +++ b/src/chain-operations/transactionHandlers.js @@ -1,5 +1,9 @@ import { convertBytes16ToUUID } from "../utils/convertUUID.js"; -import { createHistoricalTransaction } from "../db/operations/create.js"; +import { + createHistoricalTransaction, + createIssuerAuthorizedSharesAdjustment, + createStockClassAuthorizedSharesAdjustment, +} from "../db/operations/create.js"; import { upsertStakeholderById, updateStockClassById, @@ -9,31 +13,21 @@ import { upsertStockReissuanceById, upsertStockRepurchaseById, upsertStockAcceptanceById, - upsertStockClassAuthorizedSharesAdjustment, - upsertIssuerAuthorizedSharesAdjustment, updateStockPlanById, - upsertStockIssuanceBySecurityId, - upsertConvertibleIssuanceBySecurityId, - upsertWarrantIssuanceBySecurityId, - upsertEquityCompensationIssuanceBySecurityId, - upsertEquityCompensationExerciseBySecurityId, + upsertStockIssuanceById, + upsertConvertibleIssuanceById, + upsertEquityCompensationIssuanceById, + upsertWarrantIssuanceById, + upsertEquityCompensationExerciseById, } from "../db/operations/update.js"; import { toDecimal } from "../utils/convertToFixedPointDecimals.js"; -import { - IssuerAuthorizedSharesAdjustment, - StockAcceptance, - StockCancellation, - StockClassAuthorizedSharesAdjustment, - StockIssuance, - StockReissuance, - StockRepurchase, - StockRetraction, - StockTransfer, - ConvertibleIssuance, - WarrantIssuance, - EquityCompensationIssuance, - EquityCompensationExercise, -} from "./structs.js"; +import * as structs from "./structs.js"; +import { v4 as uuid } from "uuid"; +import StockPlanPoolAdjustment from "../db/objects/transactions/adjustment/StockPlanPoolAdjustment.js"; +import StockClassAuthorizedSharesAdjustment from "../db/objects/transactions/adjustment/StockClassAuthorizedSharesAdjustment.js"; +import IssuerAuthorizedSharesAdjustment from "../db/objects/transactions/adjustment/IssuerAuthorizedSharesAdjustment.js"; +import StockConsolidation from "../db/objects/transactions/consolidation/index.js"; + const options = { year: "numeric", month: "long", @@ -45,13 +39,15 @@ const options = { export const handleStockIssuance = async (stock, issuerId, timestamp) => { console.log("StockIssuanceCreated Event Emitted!", stock); - const { stock_class_id, share_price, quantity, stakeholder_id, security_id } = stock; + const { id, stock_class_id, share_price, quantity, stakeholder_id, security_id, custom_id } = stock; + const _id = convertBytes16ToUUID(id); const _security_id = convertBytes16ToUUID(security_id); const chainDate = new Date(timestamp * 1000).toISOString().split("T")[0]; const _stakeholder_id = convertBytes16ToUUID(stakeholder_id); - const createdStockIssuance = await upsertStockIssuanceBySecurityId(_security_id, { + const createdStockIssuance = await upsertStockIssuanceById(_id, { + id: _id, stock_class_id: convertBytes16ToUUID(stock_class_id), share_price: { amount: toDecimal(share_price).toString(), @@ -63,12 +59,7 @@ export const handleStockIssuance = async (stock, issuerId, timestamp) => { date: chainDate, issuer: issuerId, is_onchain_synced: true, - }); - - await createHistoricalTransaction({ - transaction: createdStockIssuance._id, - issuer: issuerId, - transactionType: "StockIssuance", + custom_id, }); console.log( @@ -80,30 +71,23 @@ export const handleStockIssuance = async (stock, issuerId, timestamp) => { export const handleStockTransfer = async (stock, issuerId) => { console.log(`Stock Transfer with quantity ${toDecimal(stock.quantity).toString()} received at `, new Date(Date.now()).toLocaleDateString()); - const id = convertBytes16ToUUID(stock.id); + const id = convertBytes16ToUUID(stock.id) || uuid(); const quantity = toDecimal(stock.quantity).toString(); const createdStockTransfer = await upsertStockTransferById(id, { _id: id, - object_type: stock.object_type, + object_type: "TX_STOCK_TRANSFER", quantity, - comments: stock.comments, - security_id: convertBytes16ToUUID(stock.security_id), - consideration_text: stock.consideration_text, - balance_security_id: convertBytes16ToUUID(stock.balance_security_id), - resulting_security_ids: convertBytes16ToUUID(stock.resulting_security_ids), + // Map blockchain fields to OCF fields + security_id: convertBytes16ToUUID(stock.consolidated_security_id), // Original position + balance_security_id: convertBytes16ToUUID(stock.remainder_security_id), // Remaining shares for partial transfer + resulting_security_ids: [convertBytes16ToUUID(stock.transferee_security_id)], // New position for transferee + // Optional fields // OCP Native Fields issuer: issuerId, is_onchain_synced: true, }); console.log("Stock Transfer reflected and validated off-chain", createdStockTransfer); - - await createHistoricalTransaction({ - transaction: createdStockTransfer._id, - issuer: createdStockTransfer.issuer, - transactionType: "StockTransfer", - }); - console.log( `✅ | StockTransfer confirmation onchain with date ${new Date(Date.now()).toLocaleDateString("en-US", options)}`, createdStockTransfer @@ -112,10 +96,8 @@ export const handleStockTransfer = async (stock, issuerId) => { export const handleStakeholder = async (id) => { try { - console.log("StakeholderCreated Event Emitted!", id); const incomingStakeholderId = convertBytes16ToUUID(id); - const stakeholder = await upsertStakeholderById(incomingStakeholderId, { is_onchain_synced: true }); - console.log("✅ | Stakeholder confirmation onchain ", stakeholder); + await upsertStakeholderById(incomingStakeholderId, { is_onchain_synced: true }); } catch (error) { throw Error("Error handing Stakeholder On Chain", error); } @@ -148,7 +130,7 @@ export const handleStockCancellation = async (stock, issuerId, timestamp) => { await createHistoricalTransaction({ transaction: createdStockCancellation._id, - issuer: createdStockCancellation.issuer, + issuer: issuerId, transactionType: "StockCancellation", }); console.log( @@ -278,71 +260,77 @@ export const handleStockAcceptance = async (stock, issuerId, timestamp) => { ); }; -export const handleStockClassAuthorizedSharesAdjusted = async (stock, issuerId, timestamp) => { - console.log("StockClassAuthorizedSharesAdjusted Event Emitted!", stock.id); - const id = convertBytes16ToUUID(stock.id); - console.log("stock price", stock.price); - - const dateOCF = new Date(timestamp * 1000).toISOString().split("T")[0]; - - const upsert = await upsertStockClassAuthorizedSharesAdjustment(id, { - _id: id, - stock_class_id: convertBytes16ToUUID(stock.stock_class_id), - object_type: stock.object_type, - comments: stock.comments, - security_id: convertBytes16ToUUID(stock.security_id), - date: dateOCF, - new_shares_authorized: toDecimal(stock.new_shares_authorized).toString(), - board_approval_date: stock.board_approval_date, - stockholder_approval_date: stock.stockholder_approval_date, - - // OCP Native Fields - issuer: issuerId, - is_onchain_synced: true, - }); +export const handleStockClassAuthorizedSharesAdjusted = async (data, issuerId, timestamp, hash) => { + console.log("StockClassAuthorizedSharesAdjusted Event Received!"); + const [id, stock_class_id, new_shares_authorized] = data; + const _id = convertBytes16ToUUID(id); + + // Update + let result; + const existingStockClassAdjustment = await StockClassAuthorizedSharesAdjustment.findById({ _id: _id }); + if (existingStockClassAdjustment) { + result = await StockClassAuthorizedSharesAdjustment.findByIdAndUpdate({ _id: _id }, { is_onchain_synced: true }, { new: true }); + console.log("[UPDATED] StockClassAuthorizedSharesAdjusted", result); + } else { + // create + result = await createStockClassAuthorizedSharesAdjustment({ + stock_class_id: convertBytes16ToUUID(stock_class_id), + new_shares_authorized: toDecimal(new_shares_authorized).toString(), + date: new Date(timestamp * 1000).toISOString().split("T")[0], + issuer: issuerId, + is_onchain_synced: true, + }); + console.log("[CREATED] StockClassAuthorizedSharesAdjusted", result); + } await createHistoricalTransaction({ - transaction: upsert._id, + transaction: result._id, issuer: issuerId, transactionType: "StockClassAuthorizedSharesAdjustment", + hash, }); - console.log( - `✅ | StockClassAuthorizedSharesAdjusted confirmation onchain with date ${new Date(Date.now()).toLocaleDateString("en-US", options)}`, - upsert - ); -}; -export const handleIssuerAuthorizedSharesAdjusted = async (issuer, issuerId, timestamp) => { - console.log("IssuerAuthorizedSharesAdjusted Event Emitted!", issuer.id); - const id = convertBytes16ToUUID(issuer.id); - console.log("stock price", issuer.price); + console.log(`✅ [CONFIRMED] StockClassAuthorizedSharesAdjusted ${new Date(Date.now()).toLocaleDateString("en-US", options)}`); +}; - const dateOCF = new Date(timestamp * 1000).toISOString().split("T")[0]; +export const handleIssuerAuthorizedSharesAdjusted = async (data, issuerId, timestamp, hash) => { + console.log("IssuerAuthorizedSharesAdjusted Event Received!"); + const [id, new_shares_authorized] = data; + const _id = convertBytes16ToUUID(id); - const upsert = await upsertIssuerAuthorizedSharesAdjustment(id, { - _id: id, - object_type: issuer.object_type, - comments: issuer.comments, - issuer_id: convertBytes16ToUUID(issuer.security_id), - date: dateOCF, - new_shares_authorized: toDecimal(issuer.new_shares_authorized).toString(), - board_approval_date: issuer.board_approval_date, - stockholder_approval_date: issuer.stockholder_approval_date, + // Try to find existing record first + const existingAdjustment = await IssuerAuthorizedSharesAdjustment.findOne({ _id: _id }); - // OCP Native Fields + const adjustmentData = { issuer: issuerId, + issuer_id: issuerId, + new_shares_authorized: toDecimal(new_shares_authorized).toString(), + date: new Date(timestamp * 1000).toISOString().split("T")[0], is_onchain_synced: true, - }); + }; + let result; + if (existingAdjustment) { + // Update existing record + result = await IssuerAuthorizedSharesAdjustment.findOneAndUpdate( + { _id: _id }, + { ...existingAdjustment, is_onchain_synced: true }, + { new: true } + ); + console.log("[UPDATED] IssuerAuthorizedSharesAdjusted", result); + } else { + // Create new record + result = await createIssuerAuthorizedSharesAdjustment(adjustmentData); + console.log("[CREATED] IssuerAuthorizedSharesAdjusted", result); + } await createHistoricalTransaction({ - transaction: upsert._id, + transaction: result._id, issuer: issuerId, transactionType: "IssuerAuthorizedSharesAdjustment", + hash, }); - console.log( - `✅ | IssuerAuthorizedSharesAdjusted confirmation onchain with date ${new Date(Date.now()).toLocaleDateString("en-US", options)}`, - upsert - ); + + console.log(`✅ [CONFIRMED] IssuerAuthorizedSharesAdjusted ${new Date(Date.now()).toLocaleDateString("en-US", options)}`); }; export const handleStockPlan = async (id, sharesReserved) => { @@ -355,15 +343,26 @@ export const handleStockPlan = async (id, sharesReserved) => { console.log("✅ | StockPlan confirmation onchain ", stockPlan); }; -export const handleConvertibleIssuance = async (convertible, issuerId, timestamp) => { +export const handleConvertibleIssuance = async (convertible, issuerId, timestamp, hash) => { console.log("ConvertibleIssuanceCreated Event Emitted!", convertible); - const { security_id, stakeholder_id, investment_amount } = convertible; - + const { + id, + security_id, + stakeholder_id, + investment_amount, + convertible_type, + conversion_triggers_mapping, + seniority, + security_law_exemptions_mapping, + custom_id, + } = convertible; const _security_id = convertBytes16ToUUID(security_id); + const _id = convertBytes16ToUUID(id); const chainDate = new Date(timestamp * 1000).toISOString().split("T")[0]; const _stakeholder_id = convertBytes16ToUUID(stakeholder_id); - const createdConvertibleIssuance = await upsertConvertibleIssuanceBySecurityId(_security_id, { + const createdConvertibleIssuance = await upsertConvertibleIssuanceById(_id, { + id: _id, investment_amount: { amount: toDecimal(investment_amount).toString(), currency: "USD", @@ -373,12 +372,18 @@ export const handleConvertibleIssuance = async (convertible, issuerId, timestamp date: chainDate, issuer: issuerId, is_onchain_synced: true, + convertible_type, + conversion_triggers_mapping, + seniority: Number(toDecimal(seniority).toString()), + security_law_exemptions_mapping, + custom_id, }); await createHistoricalTransaction({ transaction: createdConvertibleIssuance._id, issuer: issuerId, transactionType: "ConvertibleIssuance", + hash, }); console.log( @@ -387,27 +392,41 @@ export const handleConvertibleIssuance = async (convertible, issuerId, timestamp ); }; -export const handleWarrantIssuance = async (warrant, issuerId, timestamp) => { +export const handleWarrantIssuance = async (warrant, issuerId, timestamp, hash) => { console.log("WarrantIssuanceCreated Event Emitted!", warrant); - const { stakeholder_id, quantity, security_id } = warrant; + const { id, stakeholder_id, quantity, security_id, purchase_price, custom_id, security_law_exemptions_mapping, exercise_triggers_mapping } = + warrant; const _security_id = convertBytes16ToUUID(security_id); + const _id = convertBytes16ToUUID(id); const chainDate = new Date(timestamp * 1000).toISOString().split("T")[0]; const _stakeholder_id = convertBytes16ToUUID(stakeholder_id); - const createdWarrantIssuance = await upsertWarrantIssuanceBySecurityId(_security_id, { + const createdWarrantIssuance = await upsertWarrantIssuanceById(_id, { + _id: _id, date: chainDate, - quantity: toDecimal(quantity).toString(), stakeholder_id: _stakeholder_id, + quantity: toDecimal(quantity).toString(), security_id: _security_id, issuer: issuerId, is_onchain_synced: true, + custom_id, + purchase_price: + purchase_price > 0 + ? { + amount: toDecimal(purchase_price).toString(), + currency: "USD", + } + : undefined, + security_law_exemptions: security_law_exemptions_mapping, + exercise_triggers: exercise_triggers_mapping, }); await createHistoricalTransaction({ transaction: createdWarrantIssuance._id, issuer: issuerId, transactionType: "WarrantIssuance", + hash, }); console.log( @@ -416,15 +435,31 @@ export const handleWarrantIssuance = async (warrant, issuerId, timestamp) => { ); }; -export const handleEquityCompensationIssuance = async (equity, issuerId, timestamp) => { +export const handleEquityCompensationIssuance = async (equity, issuerId, timestamp, hash) => { console.log("EquityCompensationIssuanceCreated Event Emitted!", equity); - const { stakeholder_id, stock_class_id, stock_plan_id, quantity, security_id } = equity; - + const { + id, + stakeholder_id, + stock_class_id, + stock_plan_id, + quantity, + security_id, + compensation_type, + exercise_price, + base_price, + expiration_date, + custom_id, + termination_exercise_windows_mapping, + security_law_exemptions_mapping, + } = equity; + + const _id = convertBytes16ToUUID(id); const _security_id = convertBytes16ToUUID(security_id); const chainDate = new Date(timestamp * 1000).toISOString().split("T")[0]; const _stakeholder_id = convertBytes16ToUUID(stakeholder_id); - const createdEquityCompIssuance = await upsertEquityCompensationIssuanceBySecurityId(_security_id, { + const createdEquityCompIssuance = await upsertEquityCompensationIssuanceById(_id, { + _id: _id, date: chainDate, stakeholder_id: _stakeholder_id, stock_class_id: convertBytes16ToUUID(stock_class_id), @@ -433,24 +468,46 @@ export const handleEquityCompensationIssuance = async (equity, issuerId, timesta security_id: _security_id, issuer: issuerId, is_onchain_synced: true, + compensation_type, + exercise_price: + exercise_price > 0 + ? { + amount: toDecimal(exercise_price).toString(), + currency: "USD", + } + : undefined, + base_price: + base_price > 0 + ? { + amount: toDecimal(base_price).toString(), + currency: "USD", + } + : undefined, + expiration_date, + termination_exercise_windows_mapping, + security_law_exemptions_mapping, + custom_id, }); await createHistoricalTransaction({ transaction: createdEquityCompIssuance._id, issuer: issuerId, transactionType: "EquityCompensationIssuance", + hash, }); }; -export const handleEquityCompensationExercise = async (exercise, issuerId, timestamp) => { +export const handleEquityCompensationExercise = async (exercise, issuerId, timestamp, hash) => { console.log("EquityCompensationExerciseCreated Event Emitted!", exercise); - const { equity_comp_security_id, resulting_stock_security_id, quantity } = exercise; + const { id, equity_comp_security_id, resulting_stock_security_id, quantity } = exercise; + const _id = convertBytes16ToUUID(id); const _equity_comp_security_id = convertBytes16ToUUID(equity_comp_security_id); const chainDate = new Date(timestamp * 1000).toISOString().split("T")[0]; const _resulting_stock_security_id = convertBytes16ToUUID(resulting_stock_security_id); - const createdExercise = await upsertEquityCompensationExerciseBySecurityId(_equity_comp_security_id, { + const createdExercise = await upsertEquityCompensationExerciseById(_id, { + id: _id, date: chainDate, equity_comp_security_id: _equity_comp_security_id, resulting_security_ids: [_resulting_stock_security_id], @@ -463,6 +520,7 @@ export const handleEquityCompensationExercise = async (exercise, issuerId, times transaction: createdExercise._id, issuer: issuerId, transactionType: "EquityCompensationExercise", + hash, }); console.log( @@ -471,33 +529,87 @@ export const handleEquityCompensationExercise = async (exercise, issuerId, times ); }; +export const handleStockConsolidation = async (data, issuerId, timestamp) => { + console.log("StockConsolidation Event Received!"); + const { security_ids, resulting_security_id } = data; + + const dateOCF = new Date(timestamp * 1000).toISOString().split("T")[0]; + const _id = uuid(); // TODO verify this. + + let result; + + // create new consolidation record + result = await StockConsolidation.create({ + _id, + security_ids: security_ids.map((id) => convertBytes16ToUUID(id)), + resulting_security_id: convertBytes16ToUUID(resulting_security_id), + date: dateOCF, + issuer: issuerId, + is_onchain_synced: true, + }); + console.log("[CREATED] StockConsolidation", result); + + console.log(`✅ [CONFIRMED] StockConsolidation ${new Date(Date.now()).toLocaleDateString("en-US", options)}`); +}; + +export const handleStockPlanPoolAdjustment = async (data, issuerId, timestamp, hash) => { + console.log("StockPlanPoolAdjustment Event Received!"); + const [id, stockPlanId, newSharesReserved] = data; + + const dateOCF = new Date(timestamp * 1000).toISOString().split("T")[0]; + const _id = convertBytes16ToUUID(id); + let result; + const exists = await StockPlanPoolAdjustment.findOne({ _id: _id }); + if (exists) { + // update + result = await StockPlanPoolAdjustment.findOneAndUpdate({ _id: _id }, { is_onchain_synced: true }); + console.log("[UPDATED] StockPlanPoolAdjustment", result); + } else { + // upsert + result = await StockPlanPoolAdjustment.create({ + stock_plan_id: stockPlanId, + shares_reserved: toDecimal(newSharesReserved).toString(), + date: dateOCF, + issuer: issuerId, + is_onchain_synced: true, + }); + console.log("[CREATED] StockPlanPoolAdjustment", result); + } + await createHistoricalTransaction({ + transaction: result._id, + issuer: issuerId, + transactionType: "StockPlanPoolAdjustment", + hash, + }); + console.log(`✅ [CONFIRMED] StockPlanPoolAdjustment ${new Date(Date.now()).toLocaleDateString("en-US", options)}`); +}; + export const contractFuncs = new Map([ ["StakeholderCreated", handleStakeholder], ["StockClassCreated", handleStockClass], ["StockPlanCreated", handleStockPlan], ]); - -// DANGEROUS DANGEROUS DANGEROUS THIS HAS TO BE IN SAME ORDER AS DiamondTxHelper:TxType Enum +// DANGEROUS DANGEROUS DANGEROUS THIS HAS TO BE IN SAME ORDER AS TxHelper.sol:TxType Enum export const txMapper = { - 1: [IssuerAuthorizedSharesAdjustment, handleIssuerAuthorizedSharesAdjusted], - 2: [StockClassAuthorizedSharesAdjustment, handleStockClassAuthorizedSharesAdjusted], - 3: [StockAcceptance, handleStockAcceptance], - 4: [StockCancellation, handleStockCancellation], - 5: [StockIssuance, handleStockIssuance], - 6: [StockReissuance, handleStockReissuance], - 7: [StockRepurchase, handleStockRepurchase], - 8: [StockRetraction, handleStockRetraction], - 9: [StockTransfer, handleStockTransfer], - 10: [ConvertibleIssuance, handleConvertibleIssuance], - 11: [EquityCompensationIssuance, handleEquityCompensationIssuance], - // 12: [null, /*TODO: StockPlanPoolAdjustment, handleStockPlanPoolAdjustment*/ null], - 13: [WarrantIssuance, handleWarrantIssuance], - 14: [EquityCompensationExercise, handleEquityCompensationExercise], + 1: [structs.IssuerAuthorizedSharesAdjustment, handleIssuerAuthorizedSharesAdjusted], + 2: [structs.StockClassAuthorizedSharesAdjustment, handleStockClassAuthorizedSharesAdjusted], + 3: [structs.StockAcceptance, handleStockAcceptance], + 4: [structs.StockCancellation, handleStockCancellation], + 5: [structs.StockIssuance, handleStockIssuance], + 6: [structs.StockReissuance, handleStockReissuance], + 7: [structs.StockRepurchase, handleStockRepurchase], + 8: [structs.StockRetraction, handleStockRetraction], + 9: [structs.StockTransfer, handleStockTransfer], + 10: [structs.ConvertibleIssuance, handleConvertibleIssuance], + 11: [structs.EquityCompensationIssuance, handleEquityCompensationIssuance], + 12: [structs.StockPlanPoolAdjustment, handleStockPlanPoolAdjustment], + 13: [structs.WarrantIssuance, handleWarrantIssuance], + 14: [structs.EquityCompensationExercise, handleEquityCompensationExercise], + 15: [structs.StockConsolidation, handleStockConsolidation], }; + // (idx => type name) derived from txMapper -export const txTypes = Object.fromEntries( - // @ts-ignore - Object.entries(txMapper).map(([i, [_, f]]) => [i, f.name.replace("handle", "")]) -); +export const txTypes = Object.fromEntries(Object.entries(txMapper).map(([i, [_, f]]) => [i, f.name.replace("handle", "")])); + // (name => handler) derived from txMapper export const txFuncs = Object.fromEntries(Object.entries(txMapper).map(([i, [_, f]]) => [txTypes[i], f])); diff --git a/src/controllers/issuerController.js b/src/controllers/issuerController.js index 536f7956..f3c6a0a8 100644 --- a/src/controllers/issuerController.js +++ b/src/controllers/issuerController.js @@ -1,6 +1,9 @@ import { toScaledBigNumber } from "../utils/convertToFixedPointDecimals.js"; -export const convertAndAdjustIssuerAuthorizedSharesOnChain = async (contract, { new_shares_authorized }) => { +import { convertUUIDToBytes16 } from "../utils/convertUUID.js"; +export const convertAndAdjustIssuerAuthorizedSharesOnChain = async (contract, { id, new_shares_authorized }) => { + const idBytes16 = convertUUIDToBytes16(id); const scaledSharesAuthorized = toScaledBigNumber(new_shares_authorized); - const tx = await contract.adjustIssuerAuthorizedShares(scaledSharesAuthorized); - await tx.wait(); + const tx = await contract.adjustIssuerAuthorizedShares(idBytes16, scaledSharesAuthorized); + const receipt = await tx.wait(); + return receipt; }; diff --git a/src/controllers/stakeholderController.js b/src/controllers/stakeholderController.js index 8ca05220..9780e2c5 100644 --- a/src/controllers/stakeholderController.js +++ b/src/controllers/stakeholderController.js @@ -4,7 +4,8 @@ import { convertUUIDToBytes16 } from "../utils/convertUUID.js"; export const convertAndReflectStakeholderOnchain = async (contract, stakeholderId) => { const stakeholderIdBytes16 = convertUUIDToBytes16(stakeholderId); const tx = await contract.createStakeholder(stakeholderIdBytes16); - await tx.wait(); + const receipt = await tx.wait(); + return receipt; }; export const addWalletToStakeholder = async (contract, id, wallet) => { @@ -12,9 +13,8 @@ export const addWalletToStakeholder = async (contract, id, wallet) => { const stakeholderIdBytes16 = convertUUIDToBytes16(id); // Second: add wallet to stakeholder onchain const tx = await contract.addWalletToStakeholder(stakeholderIdBytes16, wallet); - await tx.wait(); - - console.log("✅ | Wallet added to stakeholder onchain"); + const receipt = await tx.wait(); + return receipt; }; export const removeWalletFromStakeholder = async (contract, id, wallet) => { @@ -22,9 +22,10 @@ export const removeWalletFromStakeholder = async (contract, id, wallet) => { const stakeholderIdBytes16 = convertUUIDToBytes16(id); // Second: remove wallet from stakeholder onchain const tx = await contract.removeWalletFromStakeholder(stakeholderIdBytes16, wallet); - await tx.wait(); + const receipt = await tx.wait(); console.log("✅ | Wallet removed from stakeholder onchain"); + return receipt; }; //TODO: to decide if we want to also return offchain data. diff --git a/src/controllers/stockClassController.js b/src/controllers/stockClassController.js index 5dadbf30..c440c393 100644 --- a/src/controllers/stockClassController.js +++ b/src/controllers/stockClassController.js @@ -3,24 +3,17 @@ import { convertUUIDToBytes16 } from "../utils/convertUUID.js"; /// @dev: controller handles conversion from OCF type to Onchain types and creates the stock class. export const convertAndReflectStockClassOnchain = async (contract, stockClass) => { - // First: convert OCF Types to Onchain Types const stockClassIdBytes16 = convertUUIDToBytes16(stockClass.id); const scaledSharePrice = toScaledBigNumber(stockClass.price_per_share.amount); const scaledShares = toScaledBigNumber(stockClass.initial_shares_authorized); - console.log("✅ | Stock Class ID offchain", stockClass.id); - console.log("✅ | Stock Class ID converted to bytes16", stockClassIdBytes16); - - // Second: create stock class onchain const tx = await contract.createStockClass(stockClassIdBytes16, stockClass.class_type, scaledSharePrice, scaledShares); - await tx.wait(); - - console.log("✅ | Stock Class created onchain"); + const receipt = await tx.wait(); + return receipt; }; //TODO: to decide if we want to also return offchain data. export const getStockClassById = async (contract, id) => { - // First: convert OCF Types to Onchain Types const stockClassIdBytes16 = convertUUIDToBytes16(id); // Second: get stock class onchain const stockClassAdded = await contract.getStockClassById(stockClassIdBytes16); @@ -28,7 +21,6 @@ export const getStockClassById = async (contract, id) => { const classType = stockClassAdded[1]; const pricePerShare = stockClassAdded[2]; const initialSharesAuthorized = stockClassAdded[3]; - console.log("Stock Class:", { stockClassId, classType, pricePerShare, initialSharesAuthorized }); return { stockClassId, classType, pricePerShare, initialSharesAuthorized }; }; @@ -39,10 +31,12 @@ export const getTotalNumberOfStockClasses = async (contract) => { return totalStockClasses.toString(); }; -export const convertAndAdjustStockClassAuthorizedSharesOnchain = async (contract, { stock_class_id, new_shares_authorized }) => { +export const convertAndAdjustStockClassAuthorizedSharesOnchain = async (contract, { id, stock_class_id, new_shares_authorized }) => { + const idBytes16 = convertUUIDToBytes16(id); const stockClassIdBytes16 = convertUUIDToBytes16(stock_class_id); const newSharesAuthorizedScaled = toScaledBigNumber(new_shares_authorized); - const tx = await contract.adjustAuthorizedShares(stockClassIdBytes16, newSharesAuthorizedScaled); - await tx.wait(); + const tx = await contract.adjustAuthorizedShares(idBytes16, stockClassIdBytes16, newSharesAuthorizedScaled); + const receipt = await tx.wait(); + return receipt; }; diff --git a/src/controllers/stockPlanController.js b/src/controllers/stockPlanController.js index dc4f8b40..c39a2d0c 100644 --- a/src/controllers/stockPlanController.js +++ b/src/controllers/stockPlanController.js @@ -13,17 +13,17 @@ export const convertAndReflectStockPlanOnchain = async (contract, stockPlan) => // Create stock plan onchain const tx = await contract.createStockPlan(stockPlanIdBytes16, stockClassIdsBytes16, sharesReserved); - await tx.wait(); + const receipt = await tx.wait(); - console.log("✅ | Stock Plan created onchain"); + return receipt; }; -export const adjustStockPlanPool = async (contract, stockPlanId, newSharesReserved) => { - const stockPlanIdBytes16 = convertUUIDToBytes16(stockPlanId); - const scaledShares = toScaledBigNumber(newSharesReserved); +export const adjustStockPlanPoolOnchain = async (contract, { id, stock_plan_id, shares_reserved }) => { + const idBytes16 = convertUUIDToBytes16(id); + const stockPlanIdBytes16 = convertUUIDToBytes16(stock_plan_id); + const scaledShares = toScaledBigNumber(shares_reserved); - const tx = await contract.adjustStockPlanPool(stockPlanIdBytes16, scaledShares); - await tx.wait(); - - console.log("✅ | Stock Plan pool adjusted onchain"); + const tx = await contract.adjustStockPlanPool(idBytes16, stockPlanIdBytes16, scaledShares); + const receipt = await tx.wait(); + return receipt; }; diff --git a/src/controllers/transactions/exerciseController.js b/src/controllers/transactions/exerciseController.js index 3a73c6a2..1753540a 100644 --- a/src/controllers/transactions/exerciseController.js +++ b/src/controllers/transactions/exerciseController.js @@ -1,21 +1,13 @@ import { convertUUIDToBytes16 } from "../../utils/convertUUID.js"; import { toScaledBigNumber } from "../../utils/convertToFixedPointDecimals.js"; -export const convertAndCreateEquityCompensationExerciseOnchain = async ( - contract, - { equity_comp_security_id, resulting_stock_security_id, quantity } -) => { - const equityCompSecurityIdBytes16 = convertUUIDToBytes16(equity_comp_security_id); - const resultingStockSecurityIdBytes16 = convertUUIDToBytes16(resulting_stock_security_id); +export const convertAndCreateEquityCompensationExerciseOnchain = async (contract, { id, security_id, resulting_security_ids, quantity }) => { + const idBytes16 = convertUUIDToBytes16(id); + const equityCompSecurityIdBytes16 = convertUUIDToBytes16(security_id); + const resultingStockSecurityIdBytes16 = convertUUIDToBytes16(resulting_security_ids[0]); const quantityScaled = toScaledBigNumber(quantity); - const tx = await contract.exerciseEquityCompensation(equityCompSecurityIdBytes16, resultingStockSecurityIdBytes16, quantityScaled); - await tx.wait(); - console.log("Transaction hash:", tx.hash); - - console.log("✅ | Exercised equity compensation onchain, unconfirmed: ", { - equity_comp_security_id, - resulting_stock_security_id, - quantity, - }); + const tx = await contract.exerciseEquityCompensation(idBytes16, equityCompSecurityIdBytes16, resultingStockSecurityIdBytes16, quantityScaled); + const receipt = await tx.wait(); + return receipt; }; diff --git a/src/controllers/transactions/issuanceController.js b/src/controllers/transactions/issuanceController.js index 51bf94d0..3f982e3d 100644 --- a/src/controllers/transactions/issuanceController.js +++ b/src/controllers/transactions/issuanceController.js @@ -2,86 +2,107 @@ import { convertUUIDToBytes16 } from "../../utils/convertUUID.js"; import { toScaledBigNumber } from "../../utils/convertToFixedPointDecimals.js"; // Stock Issuance -export const convertAndCreateIssuanceStockOnchain = async (contract, { security_id, stock_class_id, stakeholder_id, quantity, share_price }) => { - const stockClassIdBytes16 = convertUUIDToBytes16(stock_class_id); - const stakeholderIdBytes16 = convertUUIDToBytes16(stakeholder_id); - const securityIdBytes16 = convertUUIDToBytes16(security_id); - const quantityScaled = toScaledBigNumber(quantity); - const sharePriceScaled = toScaledBigNumber(share_price.amount); - - const tx = await contract.issueStock(stockClassIdBytes16, sharePriceScaled, quantityScaled, stakeholderIdBytes16, securityIdBytes16); - await tx.wait(); - console.log("Transaction hash:", tx.hash); - - console.log("✅ | Issued stock onchain, unconfirmed: ", { - security_id, - stock_class_id, - stakeholder_id, - quantity, - share_price, +export const convertAndCreateIssuanceStockOnchain = async ( + contract, + { id, security_id, stock_class_id, stakeholder_id, quantity, share_price, custom_id = "" } +) => { + console.log("data to save", { + id: convertUUIDToBytes16(id), + stock_class_id: convertUUIDToBytes16(stock_class_id), + share_price: toScaledBigNumber(share_price.amount), + quantity: toScaledBigNumber(quantity), + stakeholder_id: convertUUIDToBytes16(stakeholder_id), + security_id: convertUUIDToBytes16(security_id), + custom_id, + stock_legend_ids_mapping: "", + security_law_exemptions_mapping: "", + }); + const tx = await contract.issueStock({ + id: convertUUIDToBytes16(id), + stock_class_id: convertUUIDToBytes16(stock_class_id), + share_price: toScaledBigNumber(share_price.amount), + quantity: toScaledBigNumber(quantity), + stakeholder_id: convertUUIDToBytes16(stakeholder_id), + security_id: convertUUIDToBytes16(security_id), + custom_id, + stock_legend_ids_mapping: "", + security_law_exemptions_mapping: "", }); + const receipt = await tx.wait(); + return receipt; }; // Convertible Issuance -export const convertAndCreateIssuanceConvertibleOnchain = async (contract, { security_id, stakeholder_id, investment_amount }) => { - const stakeholderIdBytes16 = convertUUIDToBytes16(stakeholder_id); - const securityIdBytes16 = convertUUIDToBytes16(security_id); - const investmentAmountScaled = toScaledBigNumber(investment_amount); - - const tx = await contract.issueConvertible(stakeholderIdBytes16, investmentAmountScaled, securityIdBytes16); - await tx.wait(); - console.log("Transaction hash:", tx.hash); - - console.log("✅ | Issued convertible onchain, unconfirmed: ", { - security_id, - stakeholder_id, - investment_amount, +export const convertAndCreateIssuanceConvertibleOnchain = async ( + contract, + { id, security_id, stakeholder_id, investment_amount, convertible_type, seniority, custom_id = "" } +) => { + const tx = await contract.issueConvertible({ + id: convertUUIDToBytes16(id), + stakeholder_id: convertUUIDToBytes16(stakeholder_id), + investment_amount: toScaledBigNumber(investment_amount.amount), + security_id: convertUUIDToBytes16(security_id), + convertible_type, + seniority: toScaledBigNumber(seniority), + custom_id, + security_law_exemptions_mapping: "", + conversion_triggers_mapping: "", }); + const receipt = await tx.wait(); + return receipt; }; // Warrant Issuance -export const convertAndCreateIssuanceWarrantOnchain = async (contract, { security_id, stakeholder_id, quantity }) => { - const stakeholderIdBytes16 = convertUUIDToBytes16(stakeholder_id); - const securityIdBytes16 = convertUUIDToBytes16(security_id); - const quantityScaled = toScaledBigNumber(quantity); - - const tx = await contract.issueWarrant(stakeholderIdBytes16, quantityScaled, securityIdBytes16); - await tx.wait(); - console.log("Transaction hash:", tx.hash); - - console.log("✅ | Issued warrant onchain, unconfirmed: ", { - security_id, - stakeholder_id, - quantity, +export const convertAndCreateIssuanceWarrantOnchain = async ( + contract, + { id, security_id, stakeholder_id, quantity, purchase_price = { amount: 0 }, custom_id = "" } +) => { + const tx = await contract.issueWarrant({ + id: convertUUIDToBytes16(id), + stakeholder_id: convertUUIDToBytes16(stakeholder_id), + quantity: toScaledBigNumber(quantity), + security_id: convertUUIDToBytes16(security_id), + purchase_price: toScaledBigNumber(purchase_price.amount), + custom_id, + security_law_exemptions_mapping: "", + exercise_triggers_mapping: "", }); + const receipt = await tx.wait(); + return receipt; }; // Equity Compensation Issuance export const convertAndCreateIssuanceEquityCompensationOnchain = async ( contract, - { security_id, stakeholder_id, stock_class_id, stock_plan_id, quantity } -) => { - const stakeholderIdBytes16 = convertUUIDToBytes16(stakeholder_id); - const securityIdBytes16 = convertUUIDToBytes16(security_id); - const stockClassIdBytes16 = convertUUIDToBytes16(stock_class_id); - const stockPlanIdBytes16 = convertUUIDToBytes16(stock_plan_id); - const quantityScaled = toScaledBigNumber(quantity); - - const tx = await contract.issueEquityCompensation( - stakeholderIdBytes16, - stockClassIdBytes16, - stockPlanIdBytes16, - quantityScaled, - securityIdBytes16 - ); - await tx.wait(); - console.log("Transaction hash:", tx.hash); - - console.log("✅ | Issued equity compensation onchain, unconfirmed: ", { + { + id, security_id, stakeholder_id, stock_class_id, stock_plan_id, quantity, + compensation_type, + exercise_price, + base_price, + expiration_date, + custom_id = "", + } +) => { + const tx = await contract.issueEquityCompensation({ + id: convertUUIDToBytes16(id), + stakeholder_id: convertUUIDToBytes16(stakeholder_id), + stock_class_id: convertUUIDToBytes16(stock_class_id), + stock_plan_id: convertUUIDToBytes16(stock_plan_id), + quantity: toScaledBigNumber(quantity), + security_id: convertUUIDToBytes16(security_id), + compensation_type, + exercise_price: toScaledBigNumber(exercise_price?.amount || 0), + base_price: toScaledBigNumber(base_price?.amount || 0), + expiration_date, + custom_id, + termination_exercise_windows_mapping: "", + security_law_exemptions_mapping: "", }); + const receipt = await tx.wait(); + return receipt; }; diff --git a/src/controllers/transactions/transferController.js b/src/controllers/transactions/transferController.js index d09f14e8..ad1dd97b 100644 --- a/src/controllers/transactions/transferController.js +++ b/src/controllers/transactions/transferController.js @@ -2,7 +2,7 @@ import { convertUUIDToBytes16 } from "../../utils/convertUUID.js"; import { toScaledBigNumber } from "../../utils/convertToFixedPointDecimals.js"; export const convertAndCreateTransferStockOnchain = async (contract, transfer) => { - const { quantity, transferorId, transfereeId, stockClassId, isBuyerVerified, sharePrice } = transfer; + const { quantity, transferorId, transfereeId, stockClassId, sharePrice } = transfer; // First: convert OCF Types to Onchain Types const transferorIdBytes16 = convertUUIDToBytes16(transferorId); @@ -12,14 +12,7 @@ export const convertAndCreateTransferStockOnchain = async (contract, transfer) = const quantityScaled = toScaledBigNumber(quantity); const sharePriceScaled = toScaledBigNumber(sharePrice); - const tx = await contract.transferStock( - transferorIdBytes16, - transfereeIdBytes16, - stockClassIdBytes16, - isBuyerVerified, - quantityScaled, - sharePriceScaled - ); + const tx = await contract.transferStock(transferorIdBytes16, transfereeIdBytes16, stockClassIdBytes16, quantityScaled, sharePriceScaled); await tx.wait(); console.log(`Initiate Stock Transfer from transferee ID: ${transfereeId} to transferor ID: ${transferorId}`); console.log(`Quantity to be transferred: ${quantity}`); diff --git a/src/db/config/mongoose.ts b/src/db/config/mongoose.ts index 08507b4c..9d599a66 100644 --- a/src/db/config/mongoose.ts +++ b/src/db/config/mongoose.ts @@ -9,18 +9,23 @@ const DATABASE_OVERRIDE = process.env.DATABASE_OVERRIDE; export const connectDB = async () => { const connectOptions = DATABASE_OVERRIDE ? { dbName: DATABASE_OVERRIDE } : {}; try { - if (!DATABASE_URL) { - throw new Error("DATABASE_URL is not defined"); - } - const sanitizedDatabaseURL = DATABASE_URL.replace(/\/\/(.*):(.*)@/, "//$1:***@"); + const sanitizedDatabaseURL = (DATABASE_URL as string).replace(/\/\/(.*):(.*)@/, "//$1:***@"); console.log(" Mongo connecting...", sanitizedDatabaseURL); - await mongoose.connect(DATABASE_URL, connectOptions); + await mongoose.connect(DATABASE_URL as string, connectOptions); console.log("✅ | Mongo connected successfully", sanitizedDatabaseURL); return mongoose.connection; } catch (error) { console.error(error); - console.error("❌ | Error connecting to Mongo", error); + console.error("❌ | Error connecting to Mongo", (error as Error).message); // Exit process with failure process.exit(1); } }; + +export const disconnectDB = async () => { + if (mongoose.connection.readyState === mongoose.ConnectionStates.connected) { + console.log("Disconnecting from Mongo..."); + await mongoose.connection.close(); + console.log("✅ | Mongo disconnected successfully"); + } +}; diff --git a/src/db/objects/Factory.js b/src/db/objects/Factory.js index 9f78c95c..5fd46017 100644 --- a/src/db/objects/Factory.js +++ b/src/db/objects/Factory.js @@ -7,6 +7,7 @@ const FactorySchema = new mongoose.Schema( object_type: { type: String, default: "FACTORY" }, implementation_address: String, factory_address: String, + chain_id: { type: Number, required: true }, }, { timestamps: true } ); diff --git a/src/db/objects/HistoricalTransaction.js b/src/db/objects/HistoricalTransaction.js index 5e32ae27..ebf7a66d 100644 --- a/src/db/objects/HistoricalTransaction.js +++ b/src/db/objects/HistoricalTransaction.js @@ -18,6 +18,7 @@ const HistoricalTransactionSchema = new mongoose.Schema( "StockAcceptance", "IssuerAuthorizedSharesAdjustment", "StockClassAuthorizedSharesAdjustment", + "StockPlanPoolAdjustment", "EquityCompensationIssuance", "EquityCompensationExercise", "ConvertibleIssuance", @@ -25,6 +26,10 @@ const HistoricalTransactionSchema = new mongoose.Schema( ], // List of possible models required: true, }, + hash: { + type: String, + required: true, + }, issuer: { type: String, ref: "Issuer", diff --git a/src/db/objects/Issuer.js b/src/db/objects/Issuer.js index 7a0851e9..087db9c2 100644 --- a/src/db/objects/Issuer.js +++ b/src/db/objects/Issuer.js @@ -21,6 +21,7 @@ const IssuerSchema = new mongoose.Schema( tx_hash: String, last_processed_block: { type: Number, default: null }, is_manifest_created: { type: Boolean, default: false }, + chain_id: { type: Number, required: true }, }, { timestamps: true } ); diff --git a/src/db/objects/transactions/adjustment/IssuerAuthorizedSharesAdjustment.js b/src/db/objects/transactions/adjustment/IssuerAuthorizedSharesAdjustment.js index b1c79529..5ae9ed48 100644 --- a/src/db/objects/transactions/adjustment/IssuerAuthorizedSharesAdjustment.js +++ b/src/db/objects/transactions/adjustment/IssuerAuthorizedSharesAdjustment.js @@ -11,6 +11,7 @@ const IssuerAuthorizedSharesAdjustmentSchema = new mongoose.Schema( new_shares_authorized: String, board_approval_date: String, stockholder_approval_date: String, + is_onchain_synced: { type: Boolean, default: false }, issuer: { type: String, ref: "Issuer", diff --git a/src/db/objects/transactions/adjustment/StockClassAuthorizedSharesAdjustment.js b/src/db/objects/transactions/adjustment/StockClassAuthorizedSharesAdjustment.js index d5318a86..bc62a5d1 100644 --- a/src/db/objects/transactions/adjustment/StockClassAuthorizedSharesAdjustment.js +++ b/src/db/objects/transactions/adjustment/StockClassAuthorizedSharesAdjustment.js @@ -11,6 +11,7 @@ const StockClassAuthorizedSharesAdjustmentSchema = new mongoose.Schema( new_shares_authorized: String, board_approval_date: String, stockholder_approval_date: String, + is_onchain_synced: { type: Boolean, default: false }, issuer: { type: String, ref: "Issuer", diff --git a/src/db/objects/transactions/adjustment/StockPlanPoolAdjustment.js b/src/db/objects/transactions/adjustment/StockPlanPoolAdjustment.js index 1a4a34aa..c465485e 100644 --- a/src/db/objects/transactions/adjustment/StockPlanPoolAdjustment.js +++ b/src/db/objects/transactions/adjustment/StockPlanPoolAdjustment.js @@ -11,6 +11,7 @@ const StockPlanPoolAdjustmentSchema = new mongoose.Schema( board_approval_date: String, stockholder_approval_date: String, shares_reserved: String, + is_onchain_synced: { type: Boolean, default: false }, issuer: { type: String, ref: "Issuer", diff --git a/src/db/objects/transactions/consolidation/index.js b/src/db/objects/transactions/consolidation/index.js new file mode 100644 index 00000000..af8fefa6 --- /dev/null +++ b/src/db/objects/transactions/consolidation/index.js @@ -0,0 +1,24 @@ +import mongoose from "mongoose"; +import { v4 as uuid } from "uuid"; + +const StockConsolidationSchema = new mongoose.Schema( + { + _id: { type: String, default: () => uuid() }, + object_type: { type: String, default: "TX_STOCK_CONSOLIDATION" }, + security_ids: [String], + resulting_security_id: String, + comments: [String], + date: String, + reason_text: String, + issuer: { + type: String, + ref: "Issuer", + }, + is_onchain_synced: { type: Boolean, default: false }, + }, + { timestamps: true } +); + +const StockConsolidation = mongoose.model("StockConsolidation", StockConsolidationSchema); + +export default StockConsolidation; diff --git a/src/db/operations/create.js b/src/db/operations/create.js index 86bacfa1..fc1bbfce 100644 --- a/src/db/operations/create.js +++ b/src/db/operations/create.js @@ -16,6 +16,8 @@ import WarrantIssuance from "../objects/transactions/issuance/WarrantIssuance.js import VestingStart from "../objects/transactions/vesting/VestingStart.js"; import EquityCompensationExercise from "../objects/transactions/exercise/EquityCompensationExercise.js"; import StockPlanPoolAdjustment from "../objects/transactions/adjustment/StockPlanPoolAdjustment.js"; +import StockClassAuthorizedSharesAdjustment from "../objects/transactions/adjustment/StockClassAuthorizedSharesAdjustment.js"; +import IssuerAuthorizedSharesAdjustment from "../objects/transactions/adjustment/IssuerAuthorizedSharesAdjustment.js"; export const createIssuer = (issuerData) => { return save(new Issuer(issuerData)); @@ -84,3 +86,11 @@ export const createEquityCompensationExercise = (exerciseData) => { export const createStockPlanPoolAdjustment = (stockPlanPoolAdjustmentData) => { return save(new StockPlanPoolAdjustment(stockPlanPoolAdjustmentData)); }; + +export const createStockClassAuthorizedSharesAdjustment = (stockClassAuthorizedSharesAdjustmentData) => { + return save(new StockClassAuthorizedSharesAdjustment(stockClassAuthorizedSharesAdjustmentData)); +}; + +export const createIssuerAuthorizedSharesAdjustment = (issuerAuthorizedSharesAdjustmentData) => { + return save(new IssuerAuthorizedSharesAdjustment(issuerAuthorizedSharesAdjustmentData)); +}; diff --git a/src/db/operations/read.js b/src/db/operations/read.js index 7f24e8f3..b3977c4d 100644 --- a/src/db/operations/read.js +++ b/src/db/operations/read.js @@ -1,6 +1,5 @@ import Factory from "../objects/Factory.js"; import HistoricalTransaction from "../objects/HistoricalTransaction.js"; -import Fairmint from "../objects/Fairmint.js"; import Issuer from "../objects/Issuer.js"; import Stakeholder from "../objects/Stakeholder.js"; import StockClass from "../objects/StockClass.js"; @@ -112,18 +111,6 @@ export const readfactories = async () => { return await find(Factory); }; -export const readFairmintDataById = async (id) => { - return await Fairmint.findById(id); -}; - -export const readFairmintDataBySecurityId = async (securityId) => { - return await Fairmint.findOne({ security_id: securityId }); -}; - -export const readFairmintDataByStakeholderId = async (stakeholderId) => { - return await Fairmint.findOne({ stakeholder_id: stakeholderId }); -}; - export const getAllStateMachineObjectsById = async (issuerId) => { const issuer = await readIssuerById(issuerId); const stockClasses = await find(StockClass, { issuer: issuerId }); @@ -168,7 +155,7 @@ export const getAllStateMachineObjectsById = async (issuerId) => { return typeCompare !== 0 ? typeCompare : new Date(a.createdAt) - new Date(b.createdAt); }); - console.log("allTransactions", allTransactions); + console.log("All Transactions:", allTransactions.length); return { issuer, diff --git a/src/db/operations/transactions.js b/src/db/operations/transactions.js index 01255a0a..a5c373ed 100644 --- a/src/db/operations/transactions.js +++ b/src/db/operations/transactions.js @@ -2,6 +2,7 @@ import * as Acceptance from "../objects/transactions/acceptance/index.js"; import * as Adjustment from "../objects/transactions/adjustment/index.js"; import * as Cancellation from "../objects/transactions/cancellation/index.js"; import * as Conversion from "../objects/transactions/conversion/index.js"; +import * as Consolidation from "../objects/transactions/consolidation/index.js"; import * as Exercise from "../objects/transactions/exercise/index.js"; import * as Issuance from "../objects/transactions/issuance/index.js"; import * as Reissuance from "../objects/transactions/reissuance/index.js"; @@ -85,6 +86,9 @@ const typeToModelType = { TX_VESTING_ACCELERATION: Vesting.VestingAcceleration, TX_VESTING_EVENT: Vesting.VestingEvent, TX_VESTING_START: Vesting.VestingStart, + + // Consolidation + TX_STOCK_CONSOLIDATION: Consolidation.default, }; const addTransactions = async (inputTransactions, issuerId) => { diff --git a/src/db/operations/update.js b/src/db/operations/update.js index 80c55212..9d6e7da9 100644 --- a/src/db/operations/update.js +++ b/src/db/operations/update.js @@ -10,8 +10,6 @@ import StockAcceptance from "../objects/transactions/acceptance/StockAcceptance. import WarrantIssuance from "../objects/transactions/issuance/WarrantIssuance.js"; import EquityCompensationIssuance from "../objects/transactions/issuance/EquityCompensationIssuance.js"; import EquityCompensationExercise from "../objects/transactions/exercise/EquityCompensationExercise.js"; -import IssuerAuthorizedSharesAdjustment from "../objects/transactions/adjustment/IssuerAuthorizedSharesAdjustment.js"; -import StockClassAuthorizedSharesAdjustment from "../objects/transactions/adjustment/StockClassAuthorizedSharesAdjustment.js"; import StockCancellation from "../objects/transactions/cancellation/StockCancellation.js"; import StockIssuance from "../objects/transactions/issuance/StockIssuance.js"; import StockReissuance from "../objects/transactions/reissuance/StockReissuance.js"; @@ -19,11 +17,8 @@ import StockRepurchase from "../objects/transactions/repurchase/StockRepurchase. import StockRetraction from "../objects/transactions/retraction/StockRetraction.js"; import StockTransfer from "../objects/transactions/transfer/StockTransfer.js"; import ConvertibleIssuance from "../objects/transactions/issuance/ConvertibleIssuance.js"; -import Fairmint from "../objects/Fairmint.js"; import { findByIdAndUpdate, findOne, findBySecurityIdAndUpdate } from "./atomic.ts"; import { createFactory } from "./create.js"; -import get from "lodash/get"; -import { v4 as uuid } from "uuid"; export const web3WaitTime = 5000; @@ -73,6 +68,10 @@ export const upsertConvertibleIssuanceBySecurityId = async (securityId, updatedD return await findBySecurityIdAndUpdate(ConvertibleIssuance, securityId, updatedData, { new: true, upsert: true }); }; +export const upsertConvertibleIssuanceById = async (id, updatedData) => { + return await findByIdAndUpdate(ConvertibleIssuance, id, updatedData, { new: true, upsert: true }); +}; + export const upsertStockIssuanceById = async (id, updatedData) => { return await findByIdAndUpdate(StockIssuance, id, updatedData, { new: true, upsert: true }); }; @@ -101,14 +100,6 @@ export const upsertStockAcceptanceById = async (id, updatedData) => { return await findByIdAndUpdate(StockAcceptance, id, updatedData, { new: true, upsert: true }); }; -export const upsertStockClassAuthorizedSharesAdjustment = async (id, updatedData) => { - return await findByIdAndUpdate(StockClassAuthorizedSharesAdjustment, id, updatedData, { new: true, upsert: true }); -}; - -export const upsertIssuerAuthorizedSharesAdjustment = async (id, updatedData) => { - return await findByIdAndUpdate(IssuerAuthorizedSharesAdjustment, id, updatedData, { new: true, upsert: true }); -}; - export const upsertFactory = async (updatedData) => { // For now, we only allow a single record in the database const existing = await findOne(Factory); @@ -118,47 +109,26 @@ export const upsertFactory = async (updatedData) => { return await createFactory(updatedData); }; -export const upsertFairmintData = async (id, updatedData = {}) => { - const existing = await findOne(Fairmint, { _id: id }); - if (existing && existing._id) { - updatedData.attributes = { - ...get(existing, "attributes", {}), - ...get(updatedData, "attributes", {}), - }; - } - return await findByIdAndUpdate(Fairmint, get(existing, "_id"), updatedData, { new: true, upsert: true }); -}; - -export const upsertFairmintDataBySecurityId = async (security_id, updatedData = {}) => { - const existing = await findOne(Fairmint, { security_id }); - if (existing && existing._id) { - updatedData.attributes = { - ...get(existing, "attributes", {}), - ...get(updatedData, "attributes", {}), - }; - } - return await findByIdAndUpdate(Fairmint, get(existing, "_id", uuid()), updatedData, { new: true, upsert: true }); -}; - -export const upsertFairmintDataByStakeholderId = async (stakeholder_id, updatedData = {}) => { - const existing = await findOne(Fairmint, { stakeholder_id }); - if (existing && existing._id) { - updatedData.attributes = { - ...get(existing, "attributes", {}), - ...get(updatedData, "attributes", {}), - }; - } - return await findByIdAndUpdate(Fairmint, get(existing, "_id", uuid()), updatedData, { new: true, upsert: true }); -}; - export const upsertWarrantIssuanceBySecurityId = async (securityId, updatedData) => { return await findBySecurityIdAndUpdate(WarrantIssuance, securityId, updatedData, { new: true, upsert: true }); }; +export const upsertWarrantIssuanceById = async (id, updatedData) => { + return await findByIdAndUpdate(WarrantIssuance, id, updatedData, { new: true, upsert: true }); +}; + export const upsertEquityCompensationIssuanceBySecurityId = async (securityId, updatedData) => { return await findBySecurityIdAndUpdate(EquityCompensationIssuance, securityId, updatedData, { new: true, upsert: true }); }; +export const upsertEquityCompensationIssuanceById = async (id, updatedData) => { + return await findByIdAndUpdate(EquityCompensationIssuance, id, updatedData, { new: true, upsert: true }); +}; + export const upsertEquityCompensationExerciseBySecurityId = async (securityId, updatedData) => { return await findBySecurityIdAndUpdate(EquityCompensationExercise, securityId, updatedData, { new: true, upsert: true }); }; + +export const upsertEquityCompensationExerciseById = async (id, updatedData) => { + return await findByIdAndUpdate(EquityCompensationExercise, id, updatedData, { new: true, upsert: true }); +}; diff --git a/src/examples/sampleData.js b/src/examples/sampleData.js index a121bee0..d7cc037d 100644 --- a/src/examples/sampleData.js +++ b/src/examples/sampleData.js @@ -36,6 +36,7 @@ export const stakeholder1 = (issuerId) => { }, issuer_assigned_id: "", stakeholder_type: "INDIVIDUAL", + current_relationship: "EMPLOYEE", // "primary_contact": { // "name": { @@ -56,7 +57,6 @@ export const stakeholder1 = (issuerId) => { }, }; }; - export const stockClassAuthorizedSharesAdjust = (issuerId, stock_class_id, new_shares_authorized, comments) => { return { issuerId, @@ -77,7 +77,6 @@ export const issuerAuthorizedSharesAdjust = (issuerId, new_shares_authorized, co }, }; }; - export const stockAccept = (issuerId, stakeholderId, stockClassId, security_id, comments) => { return { issuerId, @@ -101,7 +100,6 @@ export const stockRetract = (issuerId, stakeholderId, stockClassId, security_id, }, }; }; - export const stockRepurchase = (issuerId, quantity, price, stakeholderId, stockClassId, security_id, comments) => { return { issuerId, @@ -128,7 +126,6 @@ export const stockReissue = (issuerId, stakeholderId, stockClassId, security_id, }, }; }; - export const stockCancel = (issuerId, quantity, stakeholderId, stockClassId, security_id, reason_text, comments) => { return { issuerId, @@ -142,7 +139,6 @@ export const stockCancel = (issuerId, quantity, stakeholderId, stockClassId, sec }, }; }; - export const stakeholder2 = (issuerId) => { return { issuerId, @@ -174,7 +170,6 @@ export const stakeholder2 = (issuerId) => { }, }; }; - export const stakeholder3 = (issuerId) => { return { issuerId, @@ -191,7 +186,6 @@ export const stakeholder3 = (issuerId) => { }, }; }; - export const stockClass = (issuerId) => { return { issuerId, @@ -215,7 +209,6 @@ export const stockClass = (issuerId) => { }, }; }; - export const stockIssuance = (issuerId, stakeholderId, stockClassId, quantity, sharePriceAmount) => { return { issuerId, @@ -245,7 +238,6 @@ export const stockIssuance = (issuerId, stakeholderId, stockClassId, quantity, s }, }; }; - export const stockTransfer = (issuerId, quantity, transferorId, transfereeId, stockClassId, sharePrice) => { return { issuerId, @@ -254,7 +246,6 @@ export const stockTransfer = (issuerId, quantity, transferorId, transfereeId, st transferorId, transfereeId, stockClassId, - isBuyerVerified: true, sharePrice, }, }; diff --git a/src/examples/testTransfer.mjs b/src/examples/testTransfer.mjs new file mode 100644 index 00000000..d0c391aa --- /dev/null +++ b/src/examples/testTransfer.mjs @@ -0,0 +1,81 @@ +import { issuer, stakeholder1, stakeholder2, stockClass, stockIssuance, stockTransfer } from "./sampleData.js"; +import axios from "axios"; +import sleep from "../utils/sleep.js"; +import { v4 as uuid } from "uuid"; + +const main = async () => { + try { + // Generate UUIDs + const issuerId = uuid(); + const stakeholder1Id = uuid(); + const stakeholder2Id = uuid(); + const stockClassId = uuid(); + + // 1. Create issuer + console.log("⏳ Creating issuer..."); + issuer.id = issuerId; + issuer.chain_id = 31337; + const issuerResponse = await axios.post("http://localhost:8080/issuer/create", issuer); + console.log("✅ Issuer created:", issuerResponse.data); + + await sleep(2000); + + // 2. Create stakeholder1 + console.log("\n⏳ Creating stakeholder1..."); + const sh1Data = stakeholder1(issuerId); + sh1Data.data.id = stakeholder1Id; + const stakeholder1Response = await axios.post("http://localhost:8080/stakeholder/create", sh1Data); + console.log("✅ Stakeholder1 created:", stakeholder1Response.data); + + await sleep(2000); + + // 3. Create stakeholder2 + console.log("\n⏳ Creating stakeholder2..."); + const sh2Data = stakeholder2(issuerId); + sh2Data.data.id = stakeholder2Id; + const stakeholder2Response = await axios.post("http://localhost:8080/stakeholder/create", sh2Data); + console.log("✅ Stakeholder2 created:", stakeholder2Response.data); + + await sleep(2000); + + // 4. Create stock class + console.log("\n⏳ Creating stock class..."); + const stockClassData = stockClass(issuerId); + stockClassData.data.id = stockClassId; + const stockClassResponse = await axios.post("http://localhost:8080/stock-class/create", stockClassData); + console.log("✅ Stock class created:", stockClassResponse.data); + + await sleep(2000); + + // 5. Create stock issuance to stakeholder1 + console.log("\n⏳ Creating stock issuance..."); + const issuanceData = stockIssuance(issuerId, stakeholder1Id, stockClassId, "1000", "1"); + const stockIssuanceResponse = await axios.post("http://localhost:8080/transactions/issuance/stock", issuanceData); + console.log("✅ Stock issued:", stockIssuanceResponse.data); + + await sleep(2000); + + // 6. Create transfer from stakeholder1 to stakeholder2 + console.log("\n⏳ Creating stock transfer..."); + const transferData = stockTransfer(issuerId, "500", stakeholder1Id, stakeholder2Id, stockClassId, "1"); + const transferResponse = await axios.post("http://localhost:8080/transactions/transfer/stock", transferData); + console.log("✅ Stock transferred:", transferResponse.data); + + console.log("\nTest completed successfully! 🎉"); + } catch (error) { + if (error.response) { + console.error("Error Response:", { + status: error.response.status, + data: error.response.data, + headers: error.response.headers, + }); + } else if (error.request) { + console.error("Error Request:", error.request); + } else { + console.error("Error Message:", error.message); + } + console.error("Error Config:", error.config); + } +}; + +main(); diff --git a/src/routes/historicalTransactions.js b/src/routes/historicalTransactions.js deleted file mode 100644 index 4778d2bc..00000000 --- a/src/routes/historicalTransactions.js +++ /dev/null @@ -1,24 +0,0 @@ -import { Router } from "express"; -import { readHistoricalTransactionByIssuerId, readIssuerById } from "../db/operations/read.js"; - -const historicalTransactions = Router(); - -historicalTransactions.get("/issuer-id/:issuerId", async (req, res) => { - const { issuerId } = req.params; - - try { - // checking issuer exists, else return error - await readIssuerById(issuerId); - - const historicalTransactions = await readHistoricalTransactionByIssuerId(issuerId); - - console.log("historicalTransactions", historicalTransactions); - - res.status(200).send(historicalTransactions); - } catch (error) { - console.error(error); - res.status(500).send(`${error}`); - } -}); - -export default historicalTransactions; diff --git a/src/routes/index.js b/src/routes/index.js index 3f539d59..a61d07a3 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -1,10 +1,10 @@ import { Router } from "express"; -import deployCapTable from "../chain-operations/deployCapTable.js"; -import { updateIssuerById } from "../db/operations/update.js"; -import seedDB, { verifyManifest } from "../db/scripts/seed.js"; -import { convertUUIDToBytes16 } from "../utils/convertUUID.js"; -import processManifest from "../utils/processManifest.js"; -import { verifyCapTable } from "../rxjs/index.js"; +// import deployCapTable from "../chain-operations/deployCapTable.js"; +// import { updateIssuerById } from "../db/operations/update.js"; +// import seedDB, { verifyManifest } from "../db/scripts/seed.js"; +// import { convertUUIDToBytes16 } from "../utils/convertUUID.js"; +// import processManifest from "../utils/processManifest.js"; +// import { verifyCapTable } from "../rxjs/index.js"; const router = Router(); @@ -12,37 +12,42 @@ router.get("/", async (req, res) => { res.status(200).send(`Welcome to the future of Transfer Agents 💸`); }); -router.post("/mint-cap-table", async (req, res) => { - try { - const manifest = await processManifest(req); - - const issuer = await seedDB(manifest); - - const issuerIdBytes16 = convertUUIDToBytes16(issuer._id); - const { address } = await deployCapTable(issuerIdBytes16, issuer.legal_name, issuer.initial_shares_authorized); - - const savedIssuerWithDeployedTo = await updateIssuerById(issuer._id, { deployed_to: address }); - res.status(200).send({ issuer: savedIssuerWithDeployedTo }); - } catch (error) { - console.error(error); - res.status(500).send({ error }); - } +router.get("/health", async (req, res) => { + res.status(200).send(`OK`); }); -router.post("/verify-cap-table", async (req, res) => { - try { - const manifest = await processManifest(req); - const data = await verifyManifest(manifest); - const result = await verifyCapTable(data); - if (result.valid) { - res.status(200).send({ valid: true }); - } else { - res.status(200).send({ valid: false, errors: result.errors }); - } - } catch (error) { - console.error({ error }); - res.status(500).send({ error: String(error), valid: false }); - } -}); +/* Outdated approach, not recommended as it doesn't translate transactions onchain */ +// router.post("/mint-cap-table", async (req, res) => { +// try { +// const manifest = await processManifest(req); + +// const issuer = await seedDB(manifest); + +// const issuerIdBytes16 = convertUUIDToBytes16(issuer._id); +// const { address } = await deployCapTable(issuerIdBytes16, issuer.legal_name, issuer.initial_shares_authorized); + +// const savedIssuerWithDeployedTo = await updateIssuerById(issuer._id, { deployed_to: address }); +// res.status(200).send({ issuer: savedIssuerWithDeployedTo }); +// } catch (error) { +// console.error(error); +// res.status(500).send({ error }); +// } +// }); + +// router.post("/verify-cap-table", async (req, res) => { +// try { +// const manifest = await processManifest(req); +// const data = await verifyManifest(manifest); +// const result = await verifyCapTable(data); +// if (result.valid) { +// res.status(200).send({ valid: true }); +// } else { +// res.status(200).send({ valid: false, errors: result.errors }); +// } +// } catch (error) { +// console.error({ error }); +// res.status(500).send({ error: String(error), valid: false }); +// } +// }); export default router; diff --git a/src/routes/issuer.js b/src/routes/issuer.js index a28d2506..8f2de78e 100644 --- a/src/routes/issuer.js +++ b/src/routes/issuer.js @@ -40,11 +40,16 @@ issuer.get("/total-number", async (req, res) => { issuer.post("/create", async (req, res) => { try { - // OCF doesn't allow extra fields in their validation + const { chain_id, ...issuerData } = req.body; + + if (!chain_id) { + return res.status(400).send({ error: "chain_id is required" }); + } + const incomingIssuerToValidate = { id: uuid(), object_type: "ISSUER", - ...req.body, + ...issuerData, }; console.log("⏳ | Issuer to validate", incomingIssuerToValidate); @@ -54,18 +59,20 @@ issuer.post("/create", async (req, res) => { if (exists && exists._id) { return res.status(200).send({ message: "issuer already exists", issuer: exists }); } + const issuerIdBytes16 = convertUUIDToBytes16(incomingIssuerToValidate.id); console.log("💾 | Issuer id in bytes16 ", issuerIdBytes16); - const { address, deployHash } = await deployCapTable(issuerIdBytes16, incomingIssuerToValidate.initial_shares_authorized); + const { address, deployHash } = await deployCapTable(issuerIdBytes16, incomingIssuerToValidate.initial_shares_authorized, Number(chain_id)); const incomingIssuerForDB = { ...incomingIssuerToValidate, deployed_to: address, tx_hash: deployHash, + chain_id: Number(chain_id), }; const issuer = await createIssuer(incomingIssuerForDB); - addAddressesToWatch(address); + addAddressesToWatch(Number(chain_id), address); console.log("✅ | Issuer created offchain:", issuer); @@ -76,5 +83,4 @@ issuer.post("/create", async (req, res) => { } }); - export default issuer; diff --git a/src/routes/stakeholder.js b/src/routes/stakeholder/base.js similarity index 80% rename from src/routes/stakeholder.js rename to src/routes/stakeholder/base.js index 0b42aa9b..00cf9b85 100644 --- a/src/routes/stakeholder.js +++ b/src/routes/stakeholder/base.js @@ -6,21 +6,19 @@ import { getStakeholderById, getTotalNumberOfStakeholders, removeWalletFromStakeholder, -} from "../controllers/stakeholderController.js"; // Importing the controller functions +} from "../../controllers/stakeholderController.js"; +import stakeholderSchema from "../../../ocf/schema/objects/Stakeholder.schema.json"; +import { createStakeholder } from "../../db/operations/create.js"; +import { readIssuerById, readStakeholderById, getAllStakeholdersByIssuerId } from "../../db/operations/read.js"; +import validateInputAgainstOCF from "../../utils/validateInputAgainstSchema.js"; -import stakeholderSchema from "../../ocf/schema/objects/Stakeholder.schema.json"; -import { createStakeholder } from "../db/operations/create.js"; -import { readIssuerById, readStakeholderById, getAllStakeholdersByIssuerId } from "../db/operations/read.js"; -import validateInputAgainstOCF from "../utils/validateInputAgainstSchema.js"; +const router = Router(); -const stakeholder = Router(); - -stakeholder.get("/", async (req, res) => { +router.get("/", async (req, res) => { res.send(`Hello stakeholder!`); }); -// offchain -stakeholder.get("/fetch-offchain/id/:id", async (req, res) => { +router.get("/fetch-offchain/id/:id", async (req, res) => { const { id } = req.params; if (!id) return res.status(400).send(`Missing id`); @@ -37,8 +35,7 @@ stakeholder.get("/fetch-offchain/id/:id", async (req, res) => { } }); -// onchain -stakeholder.get("/id/:id", async (req, res) => { +router.get("/id/:id", async (req, res) => { const { contract } = req; const { id } = req.params; @@ -52,14 +49,14 @@ stakeholder.get("/id/:id", async (req, res) => { } }); -stakeholder.get("/fetch-all", async (req, res) => { +router.get("/fetch-all", async (req, res) => { const { issuerId } = req.body; console.log("calling fetch all issuers"); try { const stakeholders = await getAllStakeholdersByIssuerId(issuerId); - console.log("stakeholders", stakeholder); + console.log("stakeholders", stakeholders); return res.status(200).send({ stakeholders }); } catch (error) { @@ -68,7 +65,7 @@ stakeholder.get("/fetch-all", async (req, res) => { } }); -stakeholder.get("/total-number", async (req, res) => { +router.get("/total-number", async (req, res) => { const { contract } = req; try { @@ -80,7 +77,7 @@ stakeholder.get("/total-number", async (req, res) => { } }); -stakeholder.post("/create", async (req, res) => { +router.post("/create", async (req, res) => { const { contract } = req; const { data, issuerId } = req.body; @@ -125,7 +122,7 @@ stakeholder.post("/create", async (req, res) => { } }); -stakeholder.post("/add-wallet", async (req, res) => { +router.post("/add-wallet", async (req, res) => { const { contract } = req; const { id, wallet } = req.body; @@ -139,7 +136,7 @@ stakeholder.post("/add-wallet", async (req, res) => { } }); -stakeholder.post("/remove-wallet", async (req, res) => { +router.post("/remove-wallet", async (req, res) => { const { contract } = req; const { id, wallet } = req.body; @@ -152,4 +149,4 @@ stakeholder.post("/remove-wallet", async (req, res) => { } }); -export default stakeholder; +export default router; diff --git a/src/routes/stakeholder/index.js b/src/routes/stakeholder/index.js new file mode 100644 index 00000000..77ac4e93 --- /dev/null +++ b/src/routes/stakeholder/index.js @@ -0,0 +1,11 @@ +import { Router } from "express"; +import baseStakeholder from "./base.js"; + +const router = Router(); + +// Mount base stakeholder routes +router.use("/", baseStakeholder); + +// Mount your companies specific routes + +export default router; diff --git a/src/routes/transactions.js b/src/routes/transactions/base.js similarity index 79% rename from src/routes/transactions.js rename to src/routes/transactions/base.js index 1d16b5e9..8bba894f 100644 --- a/src/routes/transactions.js +++ b/src/routes/transactions/base.js @@ -1,42 +1,44 @@ import { Router } from "express"; import { v4 as uuid } from "uuid"; -import stockAcceptanceSchema from "../../ocf/schema/objects/transactions/acceptance/StockAcceptance.schema.json"; -import issuerAuthorizedSharesAdjustmentSchema from "../../ocf/schema/objects/transactions/adjustment/IssuerAuthorizedSharesAdjustment.schema.json"; -import stockClassAuthorizedSharesAdjustmentSchema from "../../ocf/schema/objects/transactions/adjustment/StockClassAuthorizedSharesAdjustment.schema.json"; -import stockCancellationSchema from "../../ocf/schema/objects/transactions/cancellation/StockCancellation.schema.json"; -import warrantIssuanceSchema from "../../ocf/schema/objects/transactions/issuance/WarrantIssuance.schema.json"; -import convertibleIssuanceSchema from "../../ocf/schema/objects/transactions/issuance/ConvertibleIssuance.schema.json"; -import equityCompensationIssuanceSchema from "../../ocf/schema/objects/transactions/issuance/EquityCompensationIssuance.schema.json"; -import stockIssuanceSchema from "../../ocf/schema/objects/transactions/issuance/StockIssuance.schema.json"; -import stockReissuanceSchema from "../../ocf/schema/objects/transactions/reissuance/StockReissuance.schema.json"; -import stockRepurchaseSchema from "../../ocf/schema/objects/transactions/repurchase/StockRepurchase.schema.json"; -import stockRetractionSchema from "../../ocf/schema/objects/transactions/retraction/StockRetraction.schema.json"; -import equityCompensationExerciseSchema from "../../ocf/schema/objects/transactions/exercise/EquityCompensationExercise.schema.json"; -import stockPlanPoolAdjustmentSchema from "../../ocf/schema/objects/transactions/adjustment/StockPlanPoolAdjustment.schema.json"; - -import { convertAndAdjustIssuerAuthorizedSharesOnChain } from "../controllers/issuerController.js"; -import { convertAndAdjustStockClassAuthorizedSharesOnchain } from "../controllers/stockClassController.js"; -import { convertAndCreateAcceptanceStockOnchain } from "../controllers/transactions/acceptanceController.js"; -import { convertAndCreateCancellationStockOnchain } from "../controllers/transactions/cancellationController.js"; +import stockAcceptanceSchema from "../../../ocf/schema/objects/transactions/acceptance/StockAcceptance.schema.json"; +import issuerAuthorizedSharesAdjustmentSchema from "../../../ocf/schema/objects/transactions/adjustment/IssuerAuthorizedSharesAdjustment.schema.json"; +import stockClassAuthorizedSharesAdjustmentSchema from "../../../ocf/schema/objects/transactions/adjustment/StockClassAuthorizedSharesAdjustment.schema.json"; +import stockCancellationSchema from "../../../ocf/schema/objects/transactions/cancellation/StockCancellation.schema.json"; +import warrantIssuanceSchema from "../../../ocf/schema/objects/transactions/issuance/WarrantIssuance.schema.json"; +import convertibleIssuanceSchema from "../../../ocf/schema/objects/transactions/issuance/ConvertibleIssuance.schema.json"; +import equityCompensationIssuanceSchema from "../../../ocf/schema/objects/transactions/issuance/EquityCompensationIssuance.schema.json"; +import stockIssuanceSchema from "../../../ocf/schema/objects/transactions/issuance/StockIssuance.schema.json"; +import stockReissuanceSchema from "../../../ocf/schema/objects/transactions/reissuance/StockReissuance.schema.json"; +import stockRepurchaseSchema from "../../../ocf/schema/objects/transactions/repurchase/StockRepurchase.schema.json"; +import stockRetractionSchema from "../../../ocf/schema/objects/transactions/retraction/StockRetraction.schema.json"; +import equityCompensationExerciseSchema from "../../../ocf/schema/objects/transactions/exercise/EquityCompensationExercise.schema.json"; +import stockPlanPoolAdjustmentSchema from "../../../ocf/schema/objects/transactions/adjustment/StockPlanPoolAdjustment.schema.json"; + +import { convertAndAdjustIssuerAuthorizedSharesOnChain } from "../../controllers/issuerController.js"; +import { convertAndAdjustStockClassAuthorizedSharesOnchain } from "../../controllers/stockClassController.js"; +import { convertAndCreateAcceptanceStockOnchain } from "../../controllers/transactions/acceptanceController.js"; +import { convertAndCreateCancellationStockOnchain } from "../../controllers/transactions/cancellationController.js"; import { convertAndCreateIssuanceConvertibleOnchain, convertAndCreateIssuanceEquityCompensationOnchain, convertAndCreateIssuanceStockOnchain, convertAndCreateIssuanceWarrantOnchain, -} from "../controllers/transactions/issuanceController.js"; -import { convertAndCreateReissuanceStockOnchain } from "../controllers/transactions/reissuanceController.js"; -import { convertAndCreateRepurchaseStockOnchain } from "../controllers/transactions/repurchaseController.js"; -import { convertAndCreateRetractionStockOnchain } from "../controllers/transactions/retractionController.js"; -import { convertAndCreateTransferStockOnchain } from "../controllers/transactions/transferController.js"; +} from "../../controllers/transactions/issuanceController.js"; +import { convertAndCreateReissuanceStockOnchain } from "../../controllers/transactions/reissuanceController.js"; +import { convertAndCreateRepurchaseStockOnchain } from "../../controllers/transactions/repurchaseController.js"; +import { convertAndCreateRetractionStockOnchain } from "../../controllers/transactions/retractionController.js"; +import { convertAndCreateTransferStockOnchain } from "../../controllers/transactions/transferController.js"; import { createConvertibleIssuance, createEquityCompensationIssuance, createWarrantIssuance, createEquityCompensationExercise, createStockIssuance, -} from "../db/operations/create.js"; -import get from "lodash/get"; + createStockClassAuthorizedSharesAdjustment, + createIssuerAuthorizedSharesAdjustment, +} from "../../db/operations/create.js"; + import { readStockPlanById, readIssuerById, @@ -46,10 +48,12 @@ import { readEquityCompensationIssuanceBySecurityId, readEquityCompensationExerciseBySecurityId, readWarrantIssuanceBySecurityId, -} from "../db/operations/read.js"; -import { createStockPlanPoolAdjustment } from "../db/operations/create.js"; -import validateInputAgainstOCF from "../utils/validateInputAgainstSchema.js"; -import { convertAndCreateEquityCompensationExerciseOnchain } from "../controllers/transactions/exerciseController"; +} from "../../db/operations/read.js"; +import { createStockPlanPoolAdjustment } from "../../db/operations/create.js"; +import validateInputAgainstOCF from "../../utils/validateInputAgainstSchema.js"; +import get from "lodash/get"; +import { convertAndCreateEquityCompensationExerciseOnchain } from "../../controllers/transactions/exerciseController"; +import { adjustStockPlanPoolOnchain } from "../../controllers/stockPlanController"; const transactions = Router(); @@ -84,6 +88,9 @@ transactions.post("/issuance/stock", async (req, res) => { stakeholder_id: incomingStockIssuance.stakeholder_id, quantity: incomingStockIssuance.quantity, share_price: incomingStockIssuance.share_price, + stock_legend_ids_mapping: incomingStockIssuance.stock_legend_ids_mapping, + custom_id: incomingStockIssuance.custom_id || "", + id: incomingStockIssuance.id, }); res.status(200).send({ stockIssuance }); @@ -103,6 +110,8 @@ transactions.post("/transfer/stock", async (req, res) => { // @dev: Transfer Validation is not possible through schema because it validates that the transfer has occurred,at this stage it has not yet. await convertAndCreateTransferStockOnchain(contract, data); + // TODO: store historical transaction + res.status(200).send("success"); } catch (error) { console.error(error); @@ -290,10 +299,14 @@ transactions.post("/adjust/issuer/authorized-shares", async (req, res) => { }; await validateInputAgainstOCF(issuerAuthorizedSharesAdj, issuerAuthorizedSharesAdjustmentSchema); + // TODO: store tranaction on db + historical transactions + const createdIssuerAdjustment = await createIssuerAuthorizedSharesAdjustment({ + ...issuerAuthorizedSharesAdj, + issuer: issuerId, + }); - await convertAndAdjustIssuerAuthorizedSharesOnChain(contract, issuerAuthorizedSharesAdj); - - res.status(200).send({ issuerAuthorizedSharesAdj }); + const receipt = await convertAndAdjustIssuerAuthorizedSharesOnChain(contract, createdIssuerAdjustment); + res.status(200).send({ ...issuerAuthorizedSharesAdj, txhash: receipt.hash }); } catch (error) { console.error(error); res.status(500).send(`${error}`); @@ -324,11 +337,14 @@ transactions.post("/adjust/stock-class/authorized-shares", async (req, res) => { return res.status(404).send({ error: "Stock class not found on OCP" }); } - await convertAndAdjustStockClassAuthorizedSharesOnchain(contract, { + const createdStockClassAdjustment = await createStockClassAuthorizedSharesAdjustment({ ...stockClassAuthorizedSharesAdjustment, + issuer: issuerId, }); - res.status(200).send({ stockClassAdjustment: stockClassAuthorizedSharesAdjustment }); + const receipt = await convertAndAdjustStockClassAuthorizedSharesOnchain(contract, createdStockClassAdjustment); + + res.status(200).send({ stockClassAdjustment: { ...stockClassAuthorizedSharesAdjustment, txhash: receipt.hash } }); } catch (error) { console.error(`error: ${error.stack}`); res.status(500).send(`${error}`); @@ -336,7 +352,7 @@ transactions.post("/adjust/stock-class/authorized-shares", async (req, res) => { }); transactions.post("/adjust/stock-plan-pool", async (req, res) => { - // const { contract } = req; + const { contract } = req; const { data, issuerId } = req.body; try { @@ -359,14 +375,14 @@ transactions.post("/adjust/stock-plan-pool", async (req, res) => { return res.status(404).send({ error: "Stock plan not found on OCP" }); } - // TODO: implement Chain OP - await createStockPlanPoolAdjustment({ ...stockPlanPoolAdjustment, issuer: issuerId, }); - res.status(200).send({ stockPlanAdjustment: stockPlanPoolAdjustment }); + const receipt = await adjustStockPlanPoolOnchain(contract, stockPlanPoolAdjustment); + + res.status(200).send({ stockPlanAdjustment: { ...stockPlanPoolAdjustment, txhash: receipt.hash } }); } catch (error) { console.error(`error: ${error.stack}`); res.status(500).send(`${error}`); @@ -427,8 +443,16 @@ transactions.post("/issuance/equity-compensation", async (req, res) => { stock_class_id: incomingEquityCompensationIssuance.stock_class_id, stock_plan_id: incomingEquityCompensationIssuance.stock_plan_id, quantity: incomingEquityCompensationIssuance.quantity, + compensation_type: incomingEquityCompensationIssuance.compensation_type, + exercise_price: incomingEquityCompensationIssuance.exercise_price, + base_price: incomingEquityCompensationIssuance.base_price, + expiration_date: incomingEquityCompensationIssuance.expiration_date, + custom_id: incomingEquityCompensationIssuance.custom_id || "", + id: incomingEquityCompensationIssuance.id, }); + // TODO: Store Historical Transactions + res.status(200).send({ equityCompensationIssuance: createdIssuance }); } catch (error) { console.error(error); @@ -474,8 +498,11 @@ transactions.post("/exercise/equity-compensation", async (req, res) => { equity_comp_security_id: incomingEquityCompensationExercise.security_id, resulting_stock_security_id: incomingEquityCompensationExercise.resulting_security_ids[0], quantity: incomingEquityCompensationExercise.quantity, + id: incomingEquityCompensationExercise.id, }); + // TODO: Store Historical Transactions + res.status(200).send({ equityCompensationExercise: createdExercise }); } catch (error) { console.error(error); @@ -515,11 +542,9 @@ transactions.post("/issuance/convertible", async (req, res) => { const createdIssuance = await createConvertibleIssuance({ ...incomingConvertibleIssuance, issuer: issuerId }); // Create convertible onchain - await convertAndCreateIssuanceConvertibleOnchain(contract, { - security_id: incomingConvertibleIssuance.security_id, - stakeholder_id: incomingConvertibleIssuance.stakeholder_id, - investment_amount: incomingConvertibleIssuance.investment_amount.amount, - }); + await convertAndCreateIssuanceConvertibleOnchain(contract, createdIssuance); + + // TODO: Store Historical Transactions res.status(200).send({ convertibleIssuance: createdIssuance }); } catch (error) { @@ -564,8 +589,13 @@ transactions.post("/issuance/warrant", async (req, res) => { security_id: incomingWarrantIssuance.security_id, stakeholder_id: incomingWarrantIssuance.stakeholder_id, quantity: incomingWarrantIssuance.quantity, + purchase_price: incomingWarrantIssuance.purchase_price, + custom_id: incomingWarrantIssuance.custom_id || "", + id: incomingWarrantIssuance.id, }); + // TODO: Store Historical Transactions + res.status(200).send({ warrantIssuance: createdIssuance }); } catch (error) { console.error(error); diff --git a/src/routes/transactions/index.js b/src/routes/transactions/index.js new file mode 100644 index 00000000..3e71cea0 --- /dev/null +++ b/src/routes/transactions/index.js @@ -0,0 +1,11 @@ +import { Router } from "express"; +import baseTransactions from "./base.js"; + +const router = Router(); + +// Mount base transactions routes +router.use("/", baseTransactions); + +// Mount your company's specific transactions routes + +export default router; diff --git a/src/rxjs/captable.js b/src/rxjs/captable.js index 3d45fbb5..c23757d6 100644 --- a/src/rxjs/captable.js +++ b/src/rxjs/captable.js @@ -240,7 +240,7 @@ export const processCaptableConvertibleIssuance = (state, transaction, stakehold }; export const processCaptableWarrantAndNonPlanAwardIssuance = (state, transaction, stakeholder, originalStockClass) => { - console.log("original stock class", originalStockClass); + // console.log("original stock class", originalStockClass); const { quantity, object_type, compensation_type, exercise_triggers } = transaction; let newSummary = { ...state.summary }; diff --git a/src/rxjs/index.js b/src/rxjs/index.js index 2a62557e..753e50ac 100644 --- a/src/rxjs/index.js +++ b/src/rxjs/index.js @@ -17,24 +17,31 @@ const createInitialState = (issuer, stockClasses, stockPlans, stakeholders) => { // Create captable state const captableState = captableInitialState(issuer, stockClasses, stockPlans, stakeholders); - + const errors = new Set(); + if (!issuer.initial_shares_authorized) { + errors.add(`Issuer ${issuer.legal_name} has no initial_shares_authorized`); + } return { issuer: { id: issuer._id, sharesAuthorized: parseInt(issuer.initial_shares_authorized), sharesIssued: 0, }, - stockClasses: stockClasses.reduce( - (acc, sc) => ({ + stockClasses: stockClasses.reduce((acc, sc) => { + if (Number(sc.initial_shares_authorized) > Number(issuer.initial_shares_authorized)) { + errors.add( + `[StockClass ${sc.id} initial_shares_authorized (${sc.initial_shares_authorized}) exceeds issuer initial_shares_authorized (${issuer.initial_shares_authorized})` + ); + } + return { ...acc, [sc.id]: { id: sc.id, sharesAuthorized: parseInt(sc.initial_shares_authorized), sharesIssued: 0, }, - }), - {} - ), + }; + }, {}), stockPlans: { "no-stock-plan": { id: "no-stock-plan", @@ -62,7 +69,7 @@ const createInitialState = (issuer, stockClasses, stockPlans, stakeholders) => { ...dashboardState, ...captableState, // Add captable specific state transactions: [], // Reset transactions array - errors: new Set(), // Reset errors array + errors, // Reset errors array }; }; @@ -195,7 +202,7 @@ const processStockIssuance = (state, transaction, stakeholder, originalStockClas // Access state stock class directly from state const stateStockClass = state.stockClasses[stock_class_id]; - console.log("stateStockClass", stateStockClass); + // console.log("stateStockClass", stateStockClass); // Validate using state data if (stateStockClass.sharesIssued + numShares > stateStockClass.sharesAuthorized) { @@ -342,7 +349,7 @@ export const processEquityCompensationExercise = (state, transaction) => { const equityGrant = state.transactions.find((tx) => tx.security_id === security_id); if (!equityGrant) { - console.log("No equity grant found for:", security_id); + // console.log("No equity grant found for:", security_id); return { ...state, errors: [...state.errors, `Exercise references non-existent equity grant: ${security_id}`], @@ -404,7 +411,7 @@ export const dashboardStats = async ({ issuer, stockClasses, stockPlans, stakeho const stateWithoutTransactions = { ...state }; delete stateWithoutTransactions.transactions; - console.log("\nProcessed transaction. New state:", JSON.stringify(stateWithoutTransactions, null, 2)); + // console.log("\nProcessed transaction. New state:", JSON.stringify(stateWithoutTransactions, null, 2)); }), map((state) => { // If there are errors, return the state as is @@ -428,7 +435,7 @@ export const dashboardStats = async ({ issuer, stockClasses, stockPlans, stakeho .filter((v) => v && v.amount) .sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); - console.log("validValuations", validValuations); + // console.log("validValuations", validValuations); return { numOfStakeholders: state.numOfStakeholders, @@ -448,7 +455,7 @@ export const dashboardStats = async ({ issuer, stockClasses, stockPlans, stakeho ) ); - console.log("finalState", finalState); + // console.log("finalState", finalState); return finalState; }; @@ -457,6 +464,9 @@ export const captableStats = async ({ issuer, stockClasses, stockPlans, stakehol // If there are no transactions, map the initial state to the required format if (transactions.length === 0) { const initialState = createInitialState(issuer, stockClasses, stockPlans, stakeholders); + if (initialState.errors.size > 0) { + return { valid: false, errors: Array.from(initialState.errors) }; + } return { isCapTableEmpty: true, summary: { @@ -488,11 +498,12 @@ export const captableStats = async ({ issuer, stockClasses, stockPlans, stakehol tap((state) => { const stateWithoutTransactions = { ...state }; delete stateWithoutTransactions.transactions; - console.log("\nProcessed transaction. New state:", JSON.stringify(stateWithoutTransactions, null, 2)); + // console.log("\nProcessed transaction. New state:", JSON.stringify(stateWithoutTransactions, null, 2)); }), map((state) => { // If there are errors, return the state as is if (state.errors.size > 0) { + console.error("Errors found in state:", Array.from(state.errors)); return state; } // Just maintain section structures without calculating totals yet @@ -641,18 +652,22 @@ export const captableStats = async ({ issuer, stockClasses, stockPlans, stakehol ) ); - console.log("finalState", finalState); + // console.log("finalState", finalState); return finalState; }; export const verifyCapTable = async (captable) => { // Format manifest and get items for each object / transaction const { issuer, stockClasses, stockPlans, stakeholders, transactions } = captable; - console.log({ captable }); + // console.log({ captable }); // If there are no transactions, map the initial state to the required format if (transactions.length === 0) { - return true; + const initialState = createInitialState(issuer, stockClasses, stockPlans, stakeholders); + if (initialState.errors.size > 0) { + return { valid: false, errors: Array.from(initialState.errors) }; + } + return { valid: true, errors: [] }; } const finalState = await lastValueFrom( @@ -673,6 +688,6 @@ export const verifyCapTable = async (captable) => { ) ); - console.log("finalState", finalState); + // console.log("finalState", finalState); return finalState; }; diff --git a/src/scripts/migrate.js b/src/scripts/migrate.js new file mode 100644 index 00000000..47090f37 --- /dev/null +++ b/src/scripts/migrate.js @@ -0,0 +1,571 @@ +import { Contract } from "ethers"; +import { readIssuerById, getAllStateMachineObjectsById, readAllIssuers } from "../db/operations/read.js"; +import { updateIssuerById } from "../db/operations/update.js"; +import deployCapTable, { facetsABI, getWallet } from "../chain-operations/deployCapTable.js"; +import { convertUUIDToBytes16 } from "../utils/convertUUID.js"; +import { convertAndReflectStockClassOnchain } from "../controllers/stockClassController.js"; +import { convertAndReflectStakeholderOnchain } from "../controllers/stakeholderController.js"; +import { convertAndReflectStockPlanOnchain } from "../controllers/stockPlanController.js"; +import { convertAndAdjustIssuerAuthorizedSharesOnChain } from "../controllers/issuerController.js"; +import { convertAndAdjustStockClassAuthorizedSharesOnchain } from "../controllers/stockClassController.js"; +import { + convertAndCreateIssuanceStockOnchain, + convertAndCreateIssuanceEquityCompensationOnchain, + convertAndCreateIssuanceConvertibleOnchain, + convertAndCreateIssuanceWarrantOnchain, +} from "../controllers/transactions/issuanceController.js"; +import { convertAndCreateEquityCompensationExerciseOnchain } from "../controllers/transactions/exerciseController.js"; +import dotenv from "dotenv"; +import mongoose from "mongoose"; +import { connectDB } from "../db/config/mongoose.ts"; +import fs from "fs"; +import path from "path"; +import chalk from "chalk"; +import readline from "readline"; +import { addAddressesToWatch, reamoveAllListeners } from "../utils/websocket.ts"; +import { validateIssuerForMigration } from "./validate.js"; + +// Load environment variables +dotenv.config(); + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, +}); + +const askQuestion = (question) => { + return new Promise((resolve) => { + rl.question(chalk.cyan(question), (answer) => { + resolve(answer); + }); + }); +}; + +async function loadOrCreateMigrationLog(issuerName) { + const migrationDir = path.join(process.cwd(), "migrations"); + const logFile = path.join(migrationDir, `${issuerName}.log.json`); + + try { + // Create migrations directory if it doesn't exist + if (!fs.existsSync(migrationDir)) { + fs.mkdirSync(migrationDir, { recursive: true }); + } + + // Try to load existing log file + if (fs.existsSync(logFile)) { + const logContent = fs.readFileSync(logFile, "utf8"); + return JSON.parse(logContent); + } + + // Create new log file if it doesn't exist + const initialLog = { + name: issuerName, + startedAt: new Date().toISOString(), + updatedAt: null, + migrated: false, + records: {}, + errors: [], + }; + fs.writeFileSync(logFile, JSON.stringify(initialLog, null, 2)); + return initialLog; + } catch (error) { + console.error("Error managing migration log:", error); + throw error; + } +} + +async function updateMigrationLog(issuerName, log, gasTracker = null) { + const logFile = path.join(process.cwd(), "migrations", `${issuerName}.log.json`); + const updatedLog = { + ...log, + updatedAt: new Date().toISOString(), + // Add gas tracking if provided + ...(gasTracker && { + gasUsed: gasTracker.gasUsed.toString(), + transactionCount: gasTracker.transactionCount, + }), + }; + fs.writeFileSync(logFile, JSON.stringify(updatedLog, null, 2)); +} + +const waitBetweenTransactions = async () => { + await new Promise((resolve) => setTimeout(resolve, 1000)); // 1 second delay +}; + +async function migrateIssuer(issuerId, gasTracker = { gasUsed: BigInt(0), transactionCount: 0 }) { + await connectDB(); + let migrationLog; + let issuer; + try { + // Load or create migration log + + // 1. Check if issuer exists in the database + + issuer = await readIssuerById(issuerId); + if (!issuer) { + throw new Error(`Issuer with ID ${issuerId} not found in database`); + } + migrationLog = await loadOrCreateMigrationLog(issuer.legal_name); + console.log("Migration log loaded:", migrationLog); + const issuerData = await getAllStateMachineObjectsById(issuerId); + const errors = await validateIssuerForMigration(issuerData); + if (errors.length > 0) { + console.log("Validation errors found"); + throw new Error(errors.join("\n")); + } + + console.log(`Found issuer: ${issuer.legal_name}`); + + if (!migrationLog.records[issuerId]) { + console.log("\nDeploying cap table..."); + const issuerIdBytes16 = convertUUIDToBytes16(issuerId); + console.log(`Address before deployment: ${issuer.deployed_to}`); + console.log(`TX Hash before deployment: ${issuer.tx_hash}`); + + const { address, deployHash, receipt } = await deployCapTable(issuerIdBytes16, issuer.initial_shares_authorized, issuer.chain_id); + trackGasUsage(receipt, gasTracker); + + await updateIssuerById(issuerId, { deployed_to: address, tx_hash: deployHash }); + + console.log(`\nCap table deployed successfully:`); + console.log(`Contract Address: ${address}`); + console.log(`Deploy Hash: ${deployHash}`); + migrationLog.address = address; + migrationLog.deployHash = deployHash; + migrationLog.records[issuerId] = true; + await updateMigrationLog(issuer.legal_name, migrationLog, gasTracker); + } + + console.log({ issuerId, address: migrationLog.address, chainId: issuer.chain_id }); + const contract = new Contract(migrationLog.address, facetsABI, await getWallet(issuer.chain_id)); + addAddressesToWatch(Number(issuer.chain_id), migrationLog.address); + + // 5. Deploy Stock Classes + console.log("\nDeploying Stock Classes..."); + const totalStockClasses = issuerData.stockClasses.length; + for (const [index, stockClass] of issuerData.stockClasses.entries()) { + console.log(`\nStock Class Progress: [${index + 1}/${totalStockClasses}]`); + if (migrationLog.records[stockClass.id]) { + console.log(`Skipping Stock Class ${stockClass.id} (already deployed)`); + continue; + } + + console.log(`Deploying Stock Class: ${stockClass.id}`); + const receipt = await convertAndReflectStockClassOnchain(contract, stockClass); + migrationLog.records[stockClass.id] = true; + + trackGasUsage(receipt, gasTracker); + await updateMigrationLog(issuer.legal_name, migrationLog, gasTracker); + await waitBetweenTransactions(); + console.log(`✅ Stock Class ${stockClass.id} deployed successfully`); + } + + // 6. Deploy Stock Plans + console.log("\nDeploying Stock Plans..."); + for (const [index, stockPlan] of issuerData.stockPlans.entries()) { + console.log(`\nStock Plan Progress: [${index + 1}/${issuerData.stockPlans.length}]`); + if (migrationLog.records[stockPlan.id]) { + console.log(`Skipping Stock Plan ${stockPlan.id} (already deployed)`); + continue; + } + + console.log(`Deploying Stock Plan: ${stockPlan.id}`); + const receipt = await convertAndReflectStockPlanOnchain(contract, stockPlan); + migrationLog.records[stockPlan.id] = true; + trackGasUsage(receipt, gasTracker); + await updateMigrationLog(issuer.legal_name, migrationLog, gasTracker); + await waitBetweenTransactions(); + console.log(`✅ Stock Plan ${stockPlan.id} deployed successfully`); + } + + // 7. Deploy Stakeholders + console.log("\nDeploying Stakeholders..."); + for (const [index, stakeholder] of issuerData.stakeholders.entries()) { + console.log(`Stakeholder Progress: [${index + 1}/${issuerData.stakeholders.length}]`); + if (migrationLog.records[stakeholder.id]) { + console.log(`Skipping Stakeholder ${stakeholder.id} (already deployed)`); + continue; + } + + console.log(`Deploying Stakeholder: ${stakeholder.id}`); + const receipt = await convertAndReflectStakeholderOnchain(contract, stakeholder.id); + migrationLog.records[stakeholder.id] = true; + trackGasUsage(receipt, gasTracker); + await updateMigrationLog(issuer.legal_name, migrationLog, gasTracker); + await waitBetweenTransactions(); + console.log(`✅ Stakeholder ${stakeholder.id} deployed successfully`); + } + + // 8. Deploy Transactions in order + console.log("\nDeploying Transactions..."); + const sortedTransactions = issuerData.transactions; + const totalTransactions = sortedTransactions.length; + const successfulTxs = []; + const failedTxs = []; + + for (let index = 0; index < sortedTransactions.length; index++) { + const tx = sortedTransactions[index]; + console.log(`\nTransaction Progress: [${index + 1}/${totalTransactions}]`); + if (migrationLog.records[tx.id]) { + console.log(`Skipping Transaction ${tx.id} (already processed)`); + continue; + } + + console.log(`Processing transaction: ${tx.object_type} (ID: ${tx.id})`); + + try { + console.log({ tx }); + let receipt; + switch (tx.object_type) { + case "TX_ISSUER_AUTHORIZED_SHARES_ADJUSTMENT": + receipt = await convertAndAdjustIssuerAuthorizedSharesOnChain(contract, tx); + break; + + case "TX_STOCK_CLASS_AUTHORIZED_SHARES_ADJUSTMENT": + receipt = await convertAndAdjustStockClassAuthorizedSharesOnchain(contract, tx); + break; + + case "TX_STOCK_PLAN_POOL_ADJUSTMENT": + receipt = await convertAndReflectStockPlanOnchain(contract, tx.stock_plan_id, tx.shares_reserved); + break; + + case "TX_STOCK_ISSUANCE": + receipt = await convertAndCreateIssuanceStockOnchain(contract, tx); + break; + + case "TX_EQUITY_COMPENSATION_ISSUANCE": + receipt = await convertAndCreateIssuanceEquityCompensationOnchain(contract, tx); + break; + + case "TX_CONVERTIBLE_ISSUANCE": + receipt = await convertAndCreateIssuanceConvertibleOnchain(contract, tx); + break; + + case "TX_EQUITY_COMPENSATION_EXERCISE": + if (tx.quantity == 0) { + errors.push(`Transaction ${tx.id} has 0 quantity`); + break; + } + if (tx.resulting_security_ids.length == 0) { + errors.push(`Transaction ${tx.id} has no resulting security ids`); + break; + } + receipt = await convertAndCreateEquityCompensationExerciseOnchain(contract, tx); + break; + + case "TX_WARRANT_ISSUANCE": + if (tx.quantity == 0) { + errors.push(`Transaction ${tx.id} has 0 quantity`); + break; + } + receipt = await convertAndCreateIssuanceWarrantOnchain(contract, tx); + break; + + default: + throw new Error(`Unhandled transaction type: ${tx.object_type}`); + } + + migrationLog.records[tx.id] = true; + trackGasUsage(receipt, gasTracker); + await updateMigrationLog(issuer.legal_name, migrationLog, gasTracker); + await waitBetweenTransactions(); + console.log(`✅ Transaction ${tx.object_type} processed successfully`); + } catch (error) { + const answer = await askQuestion( + `\nError processing ${tx.object_type}. Would you like to:\n` + + ` ${chalk.yellow("s")} - Skip this transaction\n` + + ` ${chalk.yellow("r")} - Retry this transaction\n` + + ` ${chalk.yellow("a")} - Abort migration\n` + + `Enter your choice: ` + ); + + if (answer.toLowerCase() === "a") { + throw error; + } else if (answer.toLowerCase() === "r") { + index--; // Retry same transaction + continue; + } + // 's' skips to next transaction + } + } + + // Transaction Deployment Summary + console.log("\n========================="); + console.log("Transaction Deploy Summary"); + console.log("========================="); + console.log(`Total Transactions: ${sortedTransactions.length}`); + console.log(`Successful: ${successfulTxs.length}`); + console.log(`Failed: ${failedTxs.length}`); + console.log(`Validation Errors: ${errors.length}`); + + if (errors.length > 0) { + console.log("\nValidation Errors:"); + console.log("------------------"); + errors.forEach((error, index) => { + console.log(`${index + 1}. ${error}`); + }); + } + + if (failedTxs.length > 0) { + console.log("\nFailed Transactions:"); + console.log("-------------------"); + failedTxs.forEach((tx, index) => { + console.log(`${index + 1}. Type: ${tx.type}`); + console.log(` ID: ${tx.id}`); + console.log(` Error: ${tx.error}`); + }); + } + + if (successfulTxs.length > 0) { + console.log("\nSuccessful Transactions:"); + console.log("----------------------"); + successfulTxs.forEach((tx, index) => { + console.log(`${index + 1}. Type: ${tx.type}`); + console.log(` ID: ${tx.id}`); + console.log(` Timestamp: ${tx.timestamp}`); + }); + } + + // Group transactions by type for detailed logging + const txByType = issuerData.transactions.reduce((acc, tx) => { + acc[tx.object_type] = (acc[tx.object_type] || 0) + 1; + return acc; + }, {}); + + console.log("\nTransactions by Type:"); + console.log("--------------------"); + Object.entries(txByType).forEach(([type, count]) => { + const successful = successfulTxs.filter((tx) => tx.type === type).length; + const failed = failedTxs.filter((tx) => tx.type === type).length; + console.log(`${type}: ${count} (Success: ${successful}, Failed: ${failed})`); + }); + + // Log summary of fetched data + console.log("\nFetched Data Summary:"); + console.log("--------------------"); + console.log(`Stock Classes: ${issuerData.stockClasses.length}`); + console.log(`Stock Plans: ${issuerData.stockPlans.length}`); + console.log(`Stakeholders: ${issuerData.stakeholders.length}`); + console.log(`Total Transactions: ${issuerData.transactions.length}`); + + // After all migrations are successful, mark as migrated in the log + migrationLog.migrated = true; + await updateMigrationLog(issuer.legal_name, migrationLog, gasTracker); + + // Update issuer in database + await updateIssuerById(issuerId, { migrated: true }); + + return { + success: true, + gasTracker, + issuerName: issuer.legal_name, + }; + } catch (error) { + if (migrationLog) { + migrationLog.errors.push({ + error: error.message, + timestamp: new Date().toISOString(), + }); + migrationLog.migrated = false; + await updateMigrationLog(issuer.legal_name, migrationLog, gasTracker); + } + console.error("Migration failed:", error); + return { success: false, gasTracker, error }; + } finally { + await mongoose.disconnect(); + } +} + +const MAX_RETRIES = 3; // Maximum number of retry attempts + +async function main() { + const gasReport = createGasReport(); + + try { + await connectDB(); + + const skipIssuers = []; + const issuers = (await readAllIssuers()) + .filter((i) => !skipIssuers.includes(i.legal_name)) + .filter((i) => { + // Check both database and log file status + const logFile = path.join(process.cwd(), "migrations", `${i.legal_name}.log.json`); + if (fs.existsSync(logFile)) { + const log = JSON.parse(fs.readFileSync(logFile, "utf8")); + return !i.migrated && !log.migrated; + } + + return !i.migrated; + }); + + console.log(chalk.blue.bold(`Found ${issuers.length} issuers to migrate.\n`)); + + const initialAnswer = await askQuestion( + `Would you like to: \n` + + ` ${chalk.yellow("a")} - Migrate all issuers\n` + + ` ${chalk.yellow("y")} - Migrate issuers one by one\n` + + ` ${chalk.yellow("q")} - Quit\n` + + `Enter your choice: ` + ); + + if (initialAnswer.toLowerCase() === "q") { + console.log(chalk.yellow("\nExiting migration process...")); + return; + } + + const migrateAll = initialAnswer.toLowerCase() === "a"; + + for (let i = 0; i < issuers.length; i++) { + const issuer = issuers[i]; + let attempt = 1; + let success = false; + + if (!migrateAll) { + const answer = await askQuestion( + `Migrate issuer ${chalk.yellow(issuer.legal_name)} (${chalk.green(`${i + 1}/${issuers.length}`)})? (y/n/q to quit): ` + ); + + if (answer.toLowerCase() === "q") { + console.log(chalk.yellow("\nExiting migration process...")); + break; + } + + if (answer.toLowerCase() !== "y") { + continue; + } + } + + while (!success && attempt <= MAX_RETRIES) { + console.log( + chalk.blue( + `\nMigrating issuer ${chalk.yellow(issuer.legal_name)} (${chalk.green( + `${i + 1}/${issuers.length}` + )}) - Attempt ${attempt}/${MAX_RETRIES}` + ) + ); + + try { + const result = await migrateIssuer(issuer.id); + if (result.success) { + console.log(chalk.green(`\n✅ Successfully migrated ${chalk.yellow(issuer.legal_name)}`)); + console.log(chalk.blue(` Gas used: ${result.gasTracker.gasUsed.toString()}`)); + console.log(chalk.blue(` Transactions: ${result.gasTracker.transactionCount}`)); + gasReport.issuers.push({ + name: issuer.legal_name, + gasUsed: result.gasTracker.gasUsed, + transactionCount: result.gasTracker.transactionCount, + }); + gasReport.totalGasUsed += result.gasTracker.gasUsed; + } + success = true; + } catch (error) { + console.error(chalk.red(`\n❌ Error migrating ${chalk.yellow(issuer.legal_name)}:`), error); + + const answer = await askQuestion( + `\nWould you like to:\n` + + ` ${chalk.yellow("c")} - Continue with next issuer\n` + + ` ${chalk.yellow("r")} - Retry this issuer\n` + + ` ${chalk.yellow("s")} - Stop migration\n` + + `Enter your choice: ` + ); + + if (answer.toLowerCase() === "s") { + console.log(chalk.yellow("\nStopping migration process...")); + break; + } else if (answer.toLowerCase() === "r") { + i--; // Retry same issuer + continue; + } + // 'c' continues to next issuer + } + } + + console.log(chalk.gray("\n-------------------\n")); + } + + // Save final gas report + const reportPath = saveGasReport(gasReport); + console.log(chalk.green.bold("\nMigration process completed.")); + console.log(chalk.blue(`Gas report saved to: ${reportPath}`)); + console.log(chalk.blue(`Total gas used: ${gasReport.totalGasUsed.toString()}`)); + console.log(chalk.blue(`Total issuers processed: ${gasReport.issuers.length}`)); + } catch (error) { + console.error(chalk.red.bold("Error during migration process:"), chalk.red(error)); + } finally { + rl.close(); + await mongoose.disconnect(); + await reamoveAllListeners(); + console.log(chalk.gray("\nExiting migration process...")); + } +} + +// To run the script from the command line, use the following command: `npx tsx src/scripts/migrate.js ` +// Allow script to be run from command line with a specific issuer ID or run all +if (process.argv[2]) { + const issuerId = process.argv[2]; + migrateIssuer(issuerId) + .then((result) => { + if (result.success) { + console.log(chalk.green("Single issuer migration completed successfully")); + const reportPath = saveGasReport({ + totalGasUsed: result.gasTracker.gasUsed, + issuers: [ + { + name: result.issuerName, + gasUsed: result.gasTracker.gasUsed, + transactionCount: result.gasTracker.transactionCount, + }, + ], + startTime: new Date().toISOString(), + endTime: new Date().toISOString(), + }); + console.log(chalk.blue(`Gas report saved to: ${reportPath}`)); + } else { + throw result.error; + } + process.exit(0); + }) + .catch((error) => { + console.error(chalk.red("Migration failed:"), error); + process.exit(1); + }); +} else { + // Run migration for all issuers + main() + .then(() => { + console.log(chalk.green("Full migration completed successfully")); + process.exit(0); + }) + .catch((error) => { + console.error(chalk.red("Migration failed:"), error); + process.exit(1); + }); +} + +function createGasReport() { + return { + totalGasUsed: BigInt(0), + issuers: [], + startTime: new Date().toISOString(), + endTime: null, + }; +} + +function saveGasReport(report) { + const reportPath = path.join(process.cwd(), "migrations", `migration-report-${new Date().toISOString().split("T")[0]}.json`); + report.endTime = new Date().toISOString(); + fs.writeFileSync( + reportPath, + JSON.stringify(report, (_, value) => (typeof value === "bigint" ? value.toString() : value), 2) + ); + return reportPath; +} + +function trackGasUsage(receipt, tracker) { + if (!receipt) { + console.warn(chalk.yellow("Warning: No receipt provided for gas tracking")); + return; + } + tracker.gasUsed += BigInt(receipt.gasUsed); + tracker.transactionCount += 1; +} diff --git a/src/scripts/validate.js b/src/scripts/validate.js new file mode 100644 index 00000000..88ae5dc9 --- /dev/null +++ b/src/scripts/validate.js @@ -0,0 +1,337 @@ +import get from "lodash/get.js"; +import { getAllStateMachineObjectsById, readAllIssuers } from "../db/operations/read.js"; +import { connectDB, disconnectDB } from "../db/config/mongoose.ts"; +import readline from "readline"; +import chalk from "chalk"; +import { captableStats } from "../rxjs/index.js"; +/** + * Validates issuer data for migration, combining RXJS validation with cap table validation + * @param {Object} issuerData - Complete issuer data to validate + * @returns {Promise} Promise resolving to array of error messages + */ +export async function validateIssuerForMigration(issuerData) { + const rxjsData = await captableStats(issuerData); + if (rxjsData?.errors?.size > 0) { + return Array.from(rxjsData.errors); + } + + return validateCapTableData(issuerData); +} + +/** + * Validates that all required fields are present and non-empty in an object + * @param {Object} object - The object to validate + * @param {string[]} fields - Array of field names that are required + * @param {string} objectType - Type of object being validated (e.g., "Transaction", "StockClass") + * @param {string} objectId - Identifier of the object being validated + * @returns {string[]} Array of error messages, empty if validation passes + */ +function validateRequiredFields(object, fields, objectType, objectId) { + const errors = []; + fields.forEach((field) => { + const value = get(object, field); + if (!value || (typeof value === "string" && !value.trim())) { + errors.push(`${objectType} ${objectId} missing required field: ${field}`); + } + }); + return errors; +} + +/** + * Validates that referenced IDs exist in their respective reference sets + * @param {Object} object - The object containing references to validate + * @param {Object.} referenceMap - Map of field names to Sets of valid reference IDs + * @param {string} objectType - Type of object being validated + * @returns {string[]} Array of error messages, empty if validation passes + */ +function validateReferences(object, referenceMap, objectType) { + const errors = []; + Object.entries(referenceMap).forEach(([field, refSet]) => { + const value = get(object, field); + if (value && !refSet.has(value)) { + errors.push(`${objectType} ${object.id} references non-existent ${field}: ${value}`); + } + }); + return errors; +} + +/** + * Validates a transaction based on its type-specific requirements + * @param {Object} tx - Transaction object to validate + * @param {Object} referenceSets - Object containing Sets of valid IDs for different entity types + * @param {Set} referenceSets.stakeholderIds - Valid stakeholder IDs + * @param {Set} referenceSets.stockClassIds - Valid stock class IDs + * @param {Set} referenceSets.stockPlanIds - Valid stock plan IDs + * @param {Array} referenceSets.transactions - Array of all transactions + * @returns {string[]} Array of error messages, empty if validation passes + */ +function validateTransactionByType(tx, referenceSets) { + const errors = []; + const { stakeholderIds, stockClassIds, stockPlanIds } = referenceSets; + + const transactionValidations = { + TX_ISSUER_AUTHORIZED_SHARES_ADJUSTMENT: { + required: ["new_shares_authorized"], + }, + TX_STOCK_CLASS_AUTHORIZED_SHARES_ADJUSTMENT: { + required: ["stock_class_id", "new_shares_authorized"], + references: { stock_class_id: stockClassIds }, + }, + TX_STOCK_PLAN_POOL_ADJUSTMENT: { + required: ["stock_plan_id", "shares_reserved"], + references: { stock_plan_id: stockPlanIds }, + }, + TX_STOCK_ISSUANCE: { + required: ["stock_class_id", "stakeholder_id", "quantity"], + references: { stock_class_id: stockClassIds, stakeholder_id: stakeholderIds }, + }, + TX_EQUITY_COMPENSATION_ISSUANCE: { + required: ["stakeholder_id", "quantity", "stock_class_id"], + references: { stock_plan_id: stockPlanIds, stakeholder_id: stakeholderIds, stock_class_id: stockClassIds }, + }, + TX_CONVERTIBLE_ISSUANCE: { + required: ["stakeholder_id", "investment_amount.amount"], + references: { stakeholder_id: stakeholderIds }, + }, + TX_WARRANT_ISSUANCE: { + required: ["quantity"], + references: { stakeholder_id: stakeholderIds }, + customValidation: (tx) => { + const errors = []; + if (tx.quantity === 0) { + errors.push(`Transaction ${tx.id} quantity has to be greater than 0`); + } + return errors; + }, + }, + TX_EQUITY_COMPENSATION_EXERCISE: { + required: ["quantity", "resulting_security_ids"], + customValidation: (tx, transactions) => { + const errors = []; + if (tx.quantity === 0) { + errors.push(`Transaction ${tx.id} has 0 quantity`); + } + if (!tx.resulting_security_ids?.length) { + errors.push(`Transaction ${tx.id} has no resulting security ids`); + return errors; + } + + // Find the resulting stock issuance transaction + const resultingStockIssuances = tx.resulting_security_ids.map((securityId) => + transactions.find((t) => t.security_id === securityId && t.object_type === "TX_STOCK_ISSUANCE") + ); + + if (resultingStockIssuances.length == 0) { + errors.push(`Transaction ${tx.id} references non-existent stock issuance with security_id: ${tx.resulting_security_ids[0]}`); + return errors; + } + + // Validate quantities match if there is only one resulting stock issuance + if (resultingStockIssuances.length === 1) { + const resultingStockIssuance = resultingStockIssuances[0]; + if (tx.quantity !== resultingStockIssuance.quantity) { + errors.push( + `${tx.object_type} - ${tx.id} quantity (${tx.quantity}) does not match resulting stock issuance quantity (${resultingStockIssuance.quantity}) resulting_security_id: ${resultingStockIssuance.security_id}` + ); + } + } + + return errors; + }, + }, + }; + + const validation = transactionValidations[tx.object_type]; + if (!validation) { + return [`Unknown transaction type: ${tx.object_type}`]; + } + + if (validation.required) { + errors.push(...validateRequiredFields(tx, validation.required, tx.object_type, tx.id)); + } + + if (validation.references) { + errors.push(...validateReferences(tx, validation.references, tx.object_type)); + } + + if (validation.customValidation) { + errors.push(...validation.customValidation(tx, referenceSets.transactions)); + } + + return errors; +} + +/** + * Validates the entire cap table data structure + * @param {Object} issuerData - Complete cap table data + * @param {Object} issuerData.issuer - Issuer information + * @param {Array} issuerData.stakeholders - Array of stakeholder objects + * @param {Array} issuerData.stockClasses - Array of stock class objects + * @param {Array} issuerData.stockPlans - Array of stock plan objects + * @param {Array} issuerData.transactions - Array of transaction objects + * @returns {Promise} Promise resolving to array of error messages + */ +export async function validateCapTableData(issuerData) { + const errors = []; + const { stakeholders, stockClasses, stockPlans, transactions } = issuerData; + + // Create reference sets + const referenceSets = { + stakeholderIds: new Set(stakeholders.map((s) => s.id)), + stockClassIds: new Set(stockClasses.map((sc) => sc.id)), + stockPlanIds: new Set(stockPlans.map((sp) => sp.id)), + securityIds: new Set(transactions.map((t) => t.security_id)), + transactions, + }; + + // Validate basic objects + errors.push( + ...validateRequiredFields(issuerData.issuer, ["initial_shares_authorized"], "Issuer", issuerData.issuer.id), + ...stockClasses.flatMap((sc) => validateRequiredFields(sc, ["initial_shares_authorized", "price_per_share.amount"], "StockClass", sc.id)), + ...stockPlans.flatMap((sp) => validateRequiredFields(sp, ["initial_shares_reserved", "stock_class_ids"], "StockPlan", sp.id)) + ); + + // Validate stock class shares don't exceed issuer authorized shares + stockClasses.forEach((stockClass) => { + if (stockClass.initial_shares_authorized > issuerData.issuer.initial_shares_authorized) { + errors.push( + `StockClass ${stockClass.id} initial_shares_authorized (${stockClass.initial_shares_authorized}) exceeds issuer initial_shares_authorized (${issuerData.issuer.initial_shares_authorized}) - issuer id: ${issuerData.issuer.id}` + ); + } + }); + + // Validate transactions + errors.push(...transactions.flatMap((tx) => validateTransactionByType(tx, referenceSets))); + + return errors; +} + +/** + * Creates a readline interface for user input + */ +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, +}); + +/** + * Prompts the user with a question and returns the answer + * @param {string} question - The question to ask the user + * @returns {Promise} The user's answer + */ +const askQuestion = (question) => { + return new Promise((resolve) => { + rl.question(chalk.cyan(question), (answer) => { + resolve(answer); + }); + }); +}; + +// To run the script from the command line, use the following command: `npx tsx src/scripts/validate.js` +const main = async () => { + try { + await connectDB(); + // Skip Protelicious USA Corp + const skipIssuers = []; + const issuers = (await readAllIssuers()).filter((i) => !skipIssuers.includes(i.legal_name)); + const globalErrors = []; + + console.log(chalk.blue.bold(`Found ${issuers.length} issuers to validate.\n`)); + + const initialAnswer = await askQuestion( + `Would you like to: \n` + + ` ${chalk.yellow("a")} - Validate all issuers\n` + + ` ${chalk.yellow("y")} - Validate issuers one by one\n` + + ` ${chalk.yellow("q")} - Quit\n` + + `Enter your choice: ` + ); + + if (initialAnswer.toLowerCase() === "q") { + console.log(chalk.yellow("\nExiting validation process...")); + return; + } + + const validateAll = initialAnswer.toLowerCase() === "a"; + + for (let i = 0; i < issuers.length; i++) { + if (issuers[i].legal_name.toLowerCase().includes("fairbnb")) { + continue; + } + const issuer = issuers[i]; + + if (!validateAll) { + const answer = await askQuestion( + `Validate issuer ${chalk.yellow(issuer.legal_name)} (${chalk.green(`${i + 1}/${issuers.length}`)})? (y/n/q to quit): ` + ); + + if (answer.toLowerCase() === "q") { + console.log(chalk.yellow("\nExiting validation process...")); + break; + } + + if (answer.toLowerCase() !== "y") { + continue; + } + } + + console.log(chalk.blue(`\nValidating issuer ${chalk.yellow(issuer.legal_name)}...`)); + const issuerData = await getAllStateMachineObjectsById(issuer.id); + const errors = await validateIssuerForMigration(issuerData); + + if (errors.length > 0) { + console.log(chalk.red(`\nFound ${errors.length} errors for issuer ${chalk.yellow(issuer.legal_name)}:`)); + errors.forEach((error, index) => { + console.log(chalk.red(`${index + 1}. ${error}`)); + }); + globalErrors.push(...errors.map((error) => `[${issuer.legal_name}] ${error}`)); + } else { + console.log(chalk.green(`\nNo errors found for ${chalk.yellow(issuer.legal_name)}`)); + } + console.log(chalk.gray("\n-------------------\n")); + } + + if (globalErrors.length > 0) { + console.log(chalk.red.bold("\nSummary of all errors found:")); + globalErrors.forEach((error, index) => { + console.log(chalk.red(`${index + 1}. ${error}`)); + }); + } else { + console.log(chalk.green.bold("\nValidation complete. No errors found.")); + } + } catch (error) { + console.error(chalk.red.bold("Error during validation:"), chalk.red(error)); + } finally { + await disconnectDB(); + rl.close(); + console.log(chalk.gray("\nExiting validation process...")); + process.exit(0); + } +}; + +// Only run if this file is being executed directly +if (import.meta.url === `file://${process.argv[1]}`) { + // To run the script from the command line, use the following command: `npx tsx src/scripts/validate.js` + if (process.argv[2]) { + // Run validation for specific issuer + main(process.argv[2]) + .then(() => { + console.log(chalk.green("Validation completed successfully")); + process.exit(0); + }) + .catch((error) => { + console.error(chalk.red("Validation failed:"), error); + process.exit(1); + }); + } else { + // Run validation for all issuers + main() + .then(() => { + console.log(chalk.green("Full validation completed successfully")); + process.exit(0); + }) + .catch((error) => { + console.error(chalk.red("Validation failed:"), error); + process.exit(1); + }); + } +} diff --git a/src/server.js b/src/server.js deleted file mode 100644 index 9875fd75..00000000 --- a/src/server.js +++ /dev/null @@ -1,11 +0,0 @@ -import { startServer } from "./app.js"; - -// Function to check if the flag is present -const isFlagPresent = (flag) => process.argv.includes(flag); - -// Setting the default value of the flag to true -const finalizedOnly = isFlagPresent("--finalized-only"); - -console.log("Finalized Only:", finalizedOnly); - -startServer(finalizedOnly); diff --git a/src/tests/integration/transactionPoller.test.ts b/src/tests/integration/transactionPoller.test.ts deleted file mode 100644 index e337beb1..00000000 --- a/src/tests/integration/transactionPoller.test.ts +++ /dev/null @@ -1,131 +0,0 @@ -import axios from "axios"; -import { pollingSleepTime } from "../../chain-operations/transactionPoller"; -import Factory from "../../db/objects/Factory"; -import { web3WaitTime } from "../../db/operations/update"; -import { issuer as exampleIssuer, stakeholder1, stakeholder2, stakeholder3, stockClass, stockIssuance, stockTransfer } from "../../scripts/sampleData"; -import sleep from "../../utils/sleep"; -import { SERVER_BASE, runLocalServer, shutdownLocalServer } from "./utils"; - - -// Pro-tip: set this to iterate faster in dev after `seedExampleData` finishes -const HARDCODED_ISSUER_ID = null; - -beforeAll(async () => { - await runLocalServer(!HARDCODED_ISSUER_ID); -}, 10000); - -afterAll(shutdownLocalServer, 10000); - -const WAIT_TIME = 1000; - -const allowPropagate = async () => { - // Ensure ethers has enough time to catch up - await sleep(WAIT_TIME); -} - -const seedExampleData = async () => { - const rec = await Factory.findOne(); - if (!rec) { - const deterministicFactory = "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9"; - const resp = await axios.post(`${SERVER_BASE}/factory/register`, {factory_address: deterministicFactory}); - console.log("Used deterministic factory address. May need to change in future", resp.data); - // throw new Error( - // `Manually create the {"implementation_address": ..., "factory_address": ...} record - // in "factories" collection. Use output of 'yarn deploy-factory' against a local 'anvil' server` - // ); - } - - const issuerResponse = await axios.post(`${SERVER_BASE}/issuer/create`, exampleIssuer); - const issuerId = issuerResponse.data.issuer._id; - console.log("✅ | Issuer response ", issuerId, issuerResponse.data); - await allowPropagate(); - - const stakeholder1Response = await axios.post(`${SERVER_BASE}/stakeholder/create`, stakeholder1(issuerId)); - const s1Id = stakeholder1Response.data.stakeholder._id; - console.log("✅ | stakeholder1Response", s1Id, stakeholder1Response.data); - await allowPropagate(); - - const stakeholder2Response = await axios.post(`${SERVER_BASE}/stakeholder/create`, stakeholder2(issuerId)); - const s2Id = stakeholder2Response.data.stakeholder._id; - console.log("✅ | stakeholder2Response", s2Id, stakeholder2Response.data); - await allowPropagate(); - - const stakeholder3Response = await axios.post(`${SERVER_BASE}/stakeholder/create`, stakeholder3(issuerId)); - const s3Id = stakeholder3Response.data.stakeholder._id; - console.log("✅ | stakeholder3Response", s3Id, stakeholder3Response.data); - await allowPropagate(); - - const stockClassResponse = await axios.post(`${SERVER_BASE}/stock-class/create`, stockClass(issuerId)); - const stockClassId = stockClassResponse.data.stockClass._id; - console.log("✅ | stockClassResponse", stockClassId, stockClassResponse.data); - await allowPropagate(); - - const stockIssuanceResponse = await axios.post( - `${SERVER_BASE}/transactions/issuance/stock`, - stockIssuance(issuerId, s1Id, stockClassId, "500", "1.2") - ); - const issuance = stockIssuanceResponse.data.stockIssuance; - console.log("✅ | stockIssuanceResponse", issuance); - await allowPropagate(); - - // TODO: Victor acceptance of issuance? - // const { security_id } = issuance; - // const stockIssuanceAcceptanceResp = await axios.post( - // `${SERVER_BASE}/transactions/accept/stock`, - // stockAccept(issuerId, s1Id, stockClassId, security_id, ["Accepted"]) - // ); - // console.log("✅ | Stock issuance acceptance response", stockIssuanceAcceptanceResp.data); - // await allowPropagate(); - - const stockTransfer1Response = await axios.post( - `${SERVER_BASE}/transactions/transfer/stock`, - stockTransfer(issuerId, "200", s1Id, s2Id, stockClassId, "4.20") - ); - console.log("✅ | stockTransfer1Response", stockTransfer1Response.data); - await allowPropagate(); - - // TODO: Victor acceptance of transfer1? - // const stockTransferAcceptanceResp = await axios.post( - // `${SERVER_BASE}/transactions/accept/stock`, - // stockAccept(issuerId, s2Id, stockClassId, security_id, ["Accepted"]) - // ); - // console.log("✅ | Stock transfer acceptance response", stockTransferAcceptanceResp.data); - // await allowPropagate(); - - const stockTransfer2Response = await axios.post( - `${SERVER_BASE}/transactions/transfer/stock`, - stockTransfer(issuerId, "125", s1Id, s3Id, stockClassId, "10.66") - ); - console.log("✅ | stockTransfer2Response", stockTransfer2Response.data); - await allowPropagate(); - - const stockTransfer3Response = await axios.post( - `${SERVER_BASE}/transactions/transfer/stock`, - stockTransfer(issuerId, "175", s1Id, s3Id, stockClassId, "8.42") - ); - console.log("✅ | stockTransfer3Response", stockTransfer3Response.data); - await allowPropagate(); - - // TODO: acceptance of transfer2? - - // Allow time for poller process to catch up - await sleep(pollingSleepTime + web3WaitTime + 2000); - - return issuerId; -} - -const checkRecs = async (issuerId) => { - const { data: {holdings} } = await axios.get(`${SERVER_BASE}/cap-table/holdings/stock?issuerId=${issuerId}`); - let portions = holdings.map(({quantity, sharePrice, stakeholder}) => { return {quantity, sharePrice: parseFloat(sharePrice.toFixed(2)), name: stakeholder.name.legal_name}; }); - portions.sort((a, b) => b.quantity - a.quantity); - expect(portions).toStrictEqual([ - {quantity: 300, sharePrice: 9.35, name: "Kent Kolze"}, - {quantity: 200, sharePrice: 4.2, name: "Victor Mimo"}, - ]); -} - -test('end to end with event processing', async () => { - const issuerId = HARDCODED_ISSUER_ID || await seedExampleData(); - await checkRecs(issuerId); - -}, WAIT_TIME * 100); diff --git a/src/tests/integration/utils.ts b/src/tests/integration/utils.ts index 0b3e7901..b877527c 100644 --- a/src/tests/integration/utils.ts +++ b/src/tests/integration/utils.ts @@ -1,5 +1,4 @@ import { connectDB } from "../../db/config/mongoose"; -import Fairmint from "../../db/objects/Fairmint"; import HistoricalTransaction from "../../db/objects/HistoricalTransaction"; import Issuer from "../../db/objects/Issuer"; import Stakeholder from "../../db/objects/Stakeholder"; @@ -29,7 +28,6 @@ const deleteAll = async () => { await Valuation.deleteMany({}); await VestingTerms.deleteMany({}); await HistoricalTransaction.deleteMany({}); - await Fairmint.deleteMany({}); await deleteAllTransactions(); // Delete all transactions }; diff --git a/src/tests/unit/transactionPoller.test.ts b/src/tests/unit/transactionPoller.test.ts deleted file mode 100644 index 343d1688..00000000 --- a/src/tests/unit/transactionPoller.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { trimEvents, txFuncs, txTypes } from "../../chain-operations/transactionPoller"; - -// TODO: if starts failing again run: yarn add --dev jest-esm-transformer -// https://jestjs.io/docs/using-matchers for more docs on `expect` - -const myEvents = [5, 6, 6, 6, 7, 7, 7].map((x, i) => { return {i, o: {blockNumber: x}}; }); - -test('trimEvents partial', () => { - // @ts-ignore - const [events, block] = trimEvents(myEvents, 2, 10); - expect(events.length).toBe(4); - expect(events).toStrictEqual(myEvents.slice(0, 4)); - expect(block).toBe(6); -}); - -test('trimEvents full', () => { - // We allow more than maxEvents in order to include all events of the last block - for (const maxEvents of [5, 6, 7, 15]) { - // @ts-ignore - const [events, block] = trimEvents(myEvents, maxEvents, 10); - expect(events.length).toBe(myEvents.length); - expect(events).toStrictEqual(myEvents); - expect(block).toBe(10); - } -}); - -test('txMapper to maps', () => { - // @ts-ignore - expect(txTypes[3n]).toBe("StockAcceptance"); - expect(txFuncs["StockAcceptance"].name).toBe("handleStockAcceptance"); -}); diff --git a/src/utils/chains.js b/src/utils/chains.js new file mode 100644 index 00000000..36fdc860 --- /dev/null +++ b/src/utils/chains.js @@ -0,0 +1,26 @@ +// Chain configuration for supported networks +export const SUPPORTED_CHAINS = { + 8453: { + // Base Mainnet + name: "Base Mainnet", + rpcUrl: process.env.BASE_RPC_URL, + wsUrl: process.env.BASE_WS_URL, + }, + 84532: { + // Base Sepolia + name: "Base Sepolia", + rpcUrl: process.env.BASE_SEPOLIA_RPC_URL, + wsUrl: process.env.BASE_SEPOLIA_WS_URL, + }, + 31337: { + // Anvil + name: "Anvil", + rpcUrl: "http://localhost:8545", + wsUrl: "ws://localhost:8545", + }, +}; + +// Get chain configuration +export function getChainConfig(chainId) { + return SUPPORTED_CHAINS[chainId]; +} diff --git a/src/utils/env.js b/src/utils/env.js index 970f7eb6..1e283bf1 100644 --- a/src/utils/env.js +++ b/src/utils/env.js @@ -1,6 +1,7 @@ import { config } from "dotenv"; import fs from "fs"; import pathTools from "path"; +import get from "lodash/get"; const splitPath = (path) => { /* @@ -38,9 +39,18 @@ export const setupEnv = () => { if (_ALREADY_SETUP) { return; } + + // If we're in a Docker environment, skip file loading + if (get(process, "env.DOCKER_ENV", false)) { + console.log("Using runtime environment variables"); + _ALREADY_SETUP = true; + return; + } + + // Fall back to .env file for local development const fileName = process.env.USE_ENV_FILE || ".env"; const path = getEnvFile(fileName); - console.log("setupEnv with:", path); + console.log("Loading from env file:", path); config({ path }); _ALREADY_SETUP = true; }; diff --git a/src/utils/websocket.ts b/src/utils/websocket.ts index de466675..c19d3dc2 100644 --- a/src/utils/websocket.ts +++ b/src/utils/websocket.ts @@ -1,98 +1,112 @@ /* eslint-disable no-case-declarations */ import { Log, AbiCoder, Block, ethers } from "ethers"; -import { Provider } from "ethers/src.ts/providers"; import getProvider from "../chain-operations/getProvider"; import get from "lodash/get.js"; import { handleStockPlan, txMapper, txTypes } from "../chain-operations/transactionHandlers"; import { handleStakeholder, handleStockClass } from "../chain-operations/transactionHandlers"; import Issuer from "../db/objects/Issuer"; +import { TxCreated, StakeholderCreated, StockClassCreated, StockPlanCreated } from "../chain-operations/topics"; -const TOPICS = { - // TODO: automatically generate these topics from the events in the contract - TxCreated: ethers.id("TxCreated(uint8,bytes)"), - StakeholderCreated: "0x53df47344d1cdf2ddb4901af5df61e37e14606bb7c8cc004d65c7c83ab3d0693", - StockClassCreated: "0xc7496d70298fcc793e1d058617af680232585e302f0185b14bba498b247a9c1d", - StockPlanCreated: ethers.id("StockPlanCreated(bytes16,uint256)"), - // IssuerCreated: "0xb8cbde9f597f493a1b4d1c4db5fded9cd26293080750a0df6b7e7097f4b680dd", // We don't receive this event because by time an issuer is created and we add it to the listener we have already missed it. -}; +const TOPICS = { TxCreated, StakeholderCreated, StockClassCreated, StockPlanCreated }; const abiCoder = new AbiCoder(); -// Create a Set to store unique addresses -const watchedAddresses = new Set(); -// Function to add new addresses to the filter (can handle single or multiple addresses) -export const addAddressesToWatch = (addresses: string | string[]) => { - const addressArray = Array.isArray(addresses) ? addresses : [addresses]; - addressArray.forEach((address) => watchedAddresses.add(address)); - updateProviderFilter(); +// Create a map to store providers and their active listeners +const providers = new Map(); +const activeListeners = new Map(); +const watchedAddressesByChain = new Map>(); + +// Function to get or create provider for a chain +const getChainProvider = (chainId: number): ethers.Provider => { + if (!providers.has(chainId)) { + providers.set(chainId, getProvider(chainId) as ethers.Provider); + } + return providers.get(chainId)!; }; -// Function to remove addresses from the filter (can handle single or multiple addresses) -export const removeAddressesToWatch = (addresses: string | string[]) => { +// Function to add new addresses to watch for a specific chain +export const addAddressesToWatch = async (chainId: number, addresses: string | string[]) => { const addressArray = Array.isArray(addresses) ? addresses : [addresses]; - addressArray.forEach((address) => watchedAddresses.delete(address)); - updateProviderFilter(); + + if (!watchedAddressesByChain.has(chainId)) { + watchedAddressesByChain.set(chainId, new Set()); + } + + const chainAddresses = watchedAddressesByChain.get(chainId)!; + addressArray.forEach((address) => chainAddresses.add(address.toLowerCase())); + + // Always reload the listener with all addresses + await setupChainListener(chainId, Array.from(chainAddresses)); }; -// let provider: Provider; -const provider = getProvider() as Provider; -let isSetup = false; +// Function to setup a single chain listener +const setupChainListener = async (chainId: number, addresses: string[]) => { + console.log("Setting up chain listener for chain", chainId, "with addresses", addresses); + const provider = getChainProvider(chainId); -// Function to update the provider filter -const updateProviderFilter = () => { - if (!provider) { - console.log("🔗 | No provider found"); - return; + if (addresses.length > 0) { + // Remove any existing listener for this chain + if (activeListeners.get(chainId)) { + await provider.removeAllListeners(); + } + + // Set up new listener + await provider.on( + { + address: addresses, + topics: [Object.values(TOPICS)], + }, + async (log: Log) => { + const block = await provider.getBlock(log.blockNumber!); + if (block) { + handleEventType(log, block, log.address); + } + } + ); + + activeListeners.set(chainId, true); + console.log(` Chain ${chainId}: Listening to ${addresses.length} contracts`); } - console.log("🔗 | Updating provider filter"); - isSetup = false; - provider.removeAllListeners(); - setupProviderListener(); }; -// Function to set up the provider listener -const setupProviderListener = () => { - if (isSetup) { - console.log("🔗 | listener already set up"); - return; - } - console.log("🔗 | Setting up provider listener"); - provider.on( - { - address: Array.from(watchedAddresses), - topics: [Object.values(TOPICS)], - }, - async (log: Log) => { - const block = await log.getBlock(); - if (!block) { - console.error("No block found"); - return; - } - const deployed_to = log.address; - await handleEventType(log, block, deployed_to); +// Function to start listening for all chains +export const startListener = async (contracts: { address: string; chain_id: number }[]) => { + // Group contracts by chain + const contractsByChain = contracts.reduce((acc, { address, chain_id }) => { + if (!acc[chain_id]) acc[chain_id] = []; + acc[chain_id].push(address); + return acc; + }, {} as Record); + + // Start one listener per chain + for (const [chainId, addresses] of Object.entries(contractsByChain)) { + const numericChainId = parseInt(chainId); + // Add addresses to watch list + if (!watchedAddressesByChain.has(numericChainId)) { + watchedAddressesByChain.set(numericChainId, new Set()); } - ); + addresses.forEach((addr) => watchedAddressesByChain.get(numericChainId)!.add(addr.toLowerCase())); - provider.on("error", (err) => { - console.error(err); - }); - isSetup = true; + const contracts = Array.from(watchedAddressesByChain.get(numericChainId) || []); + // Setup single listener for this chain + await setupChainListener(numericChainId, contracts); + } }; -export const startListener = async (contracts: string[]) => { - if (isSetup) { - console.log("🔗 | Listener already setup"); - return; +export const reamoveAllListeners = async () => { + for (const [chainId, provider] of providers.entries()) { + console.log(`Removing listeners for chain ${chainId}...`); + await provider.removeAllListeners(); + providers.delete(chainId); + activeListeners.set(chainId, false); } - console.log("🔗 | Starting listener"); - addAddressesToWatch(contracts); - setupProviderListener(); }; +// Update handleEventType to include chainId const handleEventType = async (log: Log, block: Block, deployed_to: string) => { const topic = get(log, "topics.0", null); - console.log("🔗 | Handling event type", topic); + console.log(" | Handling event type", topic); switch (topic) { case TOPICS.StockClassCreated: const stockClassIdBytes = get(log, "topics.1", null); @@ -115,7 +129,7 @@ const handleEventType = async (log: Log, block: Block, deployed_to: string) => { break; case TOPICS.TxCreated: - console.log("🔗 | TxCreated event"); + console.log(" | TxCreated event"); const issuer = await Issuer.findOne({ deployed_to }); if (!issuer) { console.error("No issuer found"); @@ -123,7 +137,7 @@ const handleEventType = async (log: Log, block: Block, deployed_to: string) => { } const issuerId = issuer._id; const decoded = abiCoder.decode(["uint8", "bytes"], log.data); - console.log("🔗 | Decoded data", decoded); + console.log(" | Decoded data", decoded); const txTypeIdx = decoded[0] as number; const txData = decoded[1] as string; @@ -140,8 +154,8 @@ const handleEventType = async (log: Log, block: Block, deployed_to: string) => { if (handleFunc) { console.log("Handling transaction:", txType); - await handleFunc(_tx.data, issuerId, _tx.timestamp); - console.log("✅ | Transaction handled:", txType); + await handleFunc(_tx.data, issuerId, _tx.timestamp, log.transactionHash); + console.log(" | Transaction handled:", txType); } else { console.error("Invalid transaction type: ", txType); throw new Error(`Invalid transaction type: "${txType}"`); diff --git a/tsconfig.json b/tsconfig.json index dfd68ebd..36fbc0c7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,23 +1,18 @@ { - "compilerOptions": { - "target": "ES2020", - "module": "ESNext", - "moduleResolution": "node", - "esModuleInterop": true, - "strict": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "outDir": "./dist", - "rootDir": "./src", - "declaration": true, - "allowJs": true, - "checkJs": false - }, - "include": [ - "src/**/*" - ], - "exclude": [ - "node_modules", - "dist" - ] -} \ No newline at end of file + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "node", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "outDir": "./dist", + "rootDir": "./src", + "declaration": true, + "allowJs": true, + "checkJs": false + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/yarn.lock b/yarn.lock index be94b947..03852135 100644 --- a/yarn.lock +++ b/yarn.lock @@ -438,9 +438,9 @@ integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== "@eslint/js@^9.7.0": - version "9.15.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.15.0.tgz#df0e24fe869143b59731942128c19938fdbadfb5" - integrity sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg== + version "9.14.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.14.0.tgz#2347a871042ebd11a00fd8c2d3d56a265ee6857e" + integrity sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg== "@fast-csv/format@4.3.5": version "4.3.5" @@ -805,9 +805,9 @@ integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== "@opentelemetry/context-async-hooks@^1.25.1": - version "1.28.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/context-async-hooks/-/context-async-hooks-1.28.0.tgz#287afda2b75cb226f70d433244c3ef6f6dd8abdd" - integrity sha512-igcl4Ve+F1N2063PJUkesk/GkYyuGIWinYkSyAFTnIj3gzrOgvOA4k747XNdL47HRRL1w/qh7UW8NDuxOLvKFA== + version "1.27.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/context-async-hooks/-/context-async-hooks-1.27.0.tgz#a18c288ac586f5385d156003d67851465b34fb73" + integrity sha512-CdZ3qmHCwNhFAzjTgHqrDQ44Qxcpz43cVxZRhOs+Ns/79ug+Mr84Bkb626bkJLkA3+BLimA5YAEVRlJC6pFb7g== "@opentelemetry/core@1.26.0": version "1.26.0" @@ -816,10 +816,10 @@ dependencies: "@opentelemetry/semantic-conventions" "1.27.0" -"@opentelemetry/core@1.28.0", "@opentelemetry/core@^1.1.0", "@opentelemetry/core@^1.25.1", "@opentelemetry/core@^1.8.0": - version "1.28.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.28.0.tgz#e97290a3e36c59480ffb2287fe2713c66749274c" - integrity sha512-ZLwRMV+fNDpVmF2WYUdBHlq0eOWtEaUJSusrzjGnBt7iSRvfjFE3RXYUZJrqou/wIDWV0DwQ5KIfYe9WXg9Xqw== +"@opentelemetry/core@1.27.0", "@opentelemetry/core@^1.1.0", "@opentelemetry/core@^1.25.1", "@opentelemetry/core@^1.8.0": + version "1.27.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.27.0.tgz#9f1701a654ab01abcebb12931b418f3393b94b75" + integrity sha512-yQPKnK5e+76XuiqUH/gKyS8wv/7qITd5ln56QkBTf3uggr0VkXOXfcaAuG330UfdYu83wsyoBwqwxigpIG+Jkg== dependencies: "@opentelemetry/semantic-conventions" "1.27.0" @@ -1070,33 +1070,28 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/redis-common/-/redis-common-0.36.2.tgz#906ac8e4d804d4109f3ebd5c224ac988276fdc47" integrity sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g== -"@opentelemetry/resources@1.28.0", "@opentelemetry/resources@^1.26.0": - version "1.28.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.28.0.tgz#c8c27ae7559c817f9d117f1bf96d76f893fb29f5" - integrity sha512-cIyXSVJjGeTICENN40YSvLDAq4Y2502hGK3iN7tfdynQLKWb3XWZQEkPc+eSx47kiy11YeFAlYkEfXwR1w8kfw== +"@opentelemetry/resources@1.27.0", "@opentelemetry/resources@^1.26.0": + version "1.27.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.27.0.tgz#1f91c270eb95be32f3511e9e6624c1c0f993c4ac" + integrity sha512-jOwt2VJ/lUD5BLc+PMNymDrUCpm5PKi1E9oSVYAvz01U/VdndGmrtV3DU1pG4AwlYhJRHbHfOUIlpBeXCPw6QQ== dependencies: - "@opentelemetry/core" "1.28.0" + "@opentelemetry/core" "1.27.0" "@opentelemetry/semantic-conventions" "1.27.0" "@opentelemetry/sdk-trace-base@^1.22", "@opentelemetry/sdk-trace-base@^1.26.0": - version "1.28.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.28.0.tgz#6195dc8cd78bd74394cf54c67c5cbd8d1528516c" - integrity sha512-ceUVWuCpIao7Y5xE02Xs3nQi0tOGmMea17ecBdwtCvdo9ekmO+ijc9RFDgfifMl7XCBf41zne/1POM3LqSTZDA== + version "1.27.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.27.0.tgz#2276e4cd0d701a8faba77382b2938853a0907b54" + integrity sha512-btz6XTQzwsyJjombpeqCX6LhiMQYpzt2pIYNPnw0IPO/3AhT6yjnf8Mnv3ZC2A4eRYOjqrg+bfaXg9XHDRJDWQ== dependencies: - "@opentelemetry/core" "1.28.0" - "@opentelemetry/resources" "1.28.0" + "@opentelemetry/core" "1.27.0" + "@opentelemetry/resources" "1.27.0" "@opentelemetry/semantic-conventions" "1.27.0" -"@opentelemetry/semantic-conventions@1.27.0": +"@opentelemetry/semantic-conventions@1.27.0", "@opentelemetry/semantic-conventions@^1.27.0": version "1.27.0" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz#1a857dcc95a5ab30122e04417148211e6f945e6c" integrity sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg== -"@opentelemetry/semantic-conventions@^1.27.0": - version "1.28.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz#337fb2bca0453d0726696e745f50064411f646d6" - integrity sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA== - "@opentelemetry/sql-common@^0.40.1": version "0.40.1" resolved "https://registry.yarnpkg.com/@opentelemetry/sql-common/-/sql-common-0.40.1.tgz#93fbc48d8017449f5b3c3274f2268a08af2b83b6" @@ -1118,17 +1113,18 @@ resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== -"@sentry/core@8.40.0": - version "8.40.0" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.40.0.tgz#cb5c02d12e29070bf88692c64cfd7db7700be4ea" - integrity sha512-u/U2CJpG/+SmTR2bPM4ZZoPYTJAOUuxzj/0IURnvI0v9+rNu939J/fzrO9huA5IJVxS5TiYykhQm7o6I3Zuo3Q== +"@sentry/core@8.38.0": + version "8.38.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.38.0.tgz#5d1b74770c79e489e786018a3e514cddeb777bcb" + integrity sha512-sGD+5TEHU9G7X7zpyaoJxpOtwjTjvOd1f/MKBrWW2vf9UbYK+GUJrOzLhMoSWp/pHSYgvObkJkDb/HwieQjvhQ== dependencies: - "@sentry/types" "8.40.0" + "@sentry/types" "8.38.0" + "@sentry/utils" "8.38.0" "@sentry/node@^8.37.1": - version "8.40.0" - resolved "https://registry.yarnpkg.com/@sentry/node/-/node-8.40.0.tgz#0dcf4ae224698191e59dba026a5249a27e98fc97" - integrity sha512-UO1jWuO+z4DnK2NYCvQQfpNbfFYgeV//cNS83QIPkj9hPIEOpUR2DAfPmI9bj2Yjdh7WE8IN9Can9xDcfJquMQ== + version "8.38.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-8.38.0.tgz#a59883b2b1b4c5515f95c65af1965df20bc14d69" + integrity sha512-nwW0XqZFQseXYn0i6i6nKPkbjgHMBEFSF9TnK6mHHqJHHObHIZ6qu5CfvGKgxATia8JPIg9NN8XcyYOnQMi07w== dependencies: "@opentelemetry/api" "^1.9.0" "@opentelemetry/context-async-hooks" "^1.25.1" @@ -1162,23 +1158,32 @@ "@opentelemetry/sdk-trace-base" "^1.26.0" "@opentelemetry/semantic-conventions" "^1.27.0" "@prisma/instrumentation" "5.19.1" - "@sentry/core" "8.40.0" - "@sentry/opentelemetry" "8.40.0" - "@sentry/types" "8.40.0" + "@sentry/core" "8.38.0" + "@sentry/opentelemetry" "8.38.0" + "@sentry/types" "8.38.0" + "@sentry/utils" "8.38.0" import-in-the-middle "^1.11.2" -"@sentry/opentelemetry@8.40.0": - version "8.40.0" - resolved "https://registry.yarnpkg.com/@sentry/opentelemetry/-/opentelemetry-8.40.0.tgz#55d19770cc2cc61084f4c04b728a550c06282ab0" - integrity sha512-kW9EBRESjNnBdj2zCqNMv8x0VIsmiALIOMpi25Dpm38IKtRg/ckQ7YOWx1lnT3iOFebO2GXUvOu+gPmuzIY2WQ== +"@sentry/opentelemetry@8.38.0": + version "8.38.0" + resolved "https://registry.yarnpkg.com/@sentry/opentelemetry/-/opentelemetry-8.38.0.tgz#b4bae78c56f72b4bdc2a921c59a53339e776582d" + integrity sha512-AfjmIf/v7+x2WplhkX66LyGKvrzzPeSgff9uJ0cFCC2s0yd1qA2VPuIwEyr5i/FOJOP5bvFr8tu/hz3LA4+F5Q== dependencies: - "@sentry/core" "8.40.0" - "@sentry/types" "8.40.0" + "@sentry/core" "8.38.0" + "@sentry/types" "8.38.0" + "@sentry/utils" "8.38.0" -"@sentry/types@8.40.0": - version "8.40.0" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.40.0.tgz#a98d2bcc48adbc066b403713688ded3ac5eb1cec" - integrity sha512-nuCf3U3deolPM9BjNnwCc33UtFl9ec15/r74ngAkNccn+A2JXdIAsDkGJMO/9mgSFykLe1QyeJ0pQFRisCGOiA== +"@sentry/types@8.38.0": + version "8.38.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.38.0.tgz#9c48734a8b4055bfd553a0141efec78e9680ed09" + integrity sha512-fP5H9ZX01W4Z/EYctk3mkSHi7d06cLcX2/UWqwdWbyPWI+pL2QpUPICeO/C+8SnmYx//wFj3qWDhyPCh1PdFAA== + +"@sentry/utils@8.38.0": + version "8.38.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.38.0.tgz#2f91ca7d044f6e17b993c866ca02a981c4c1bc25" + integrity sha512-3X7MgIKIx+2q5Al7QkhaRB4wV6DvzYsaeIwdqKUzGLuRjXmNgJrLoU87TAwQRmZ6Wr3IoEpThZZMNrzYPXxArw== + dependencies: + "@sentry/types" "8.38.0" "@sideway/address@^4.1.5": version "4.1.5" @@ -1320,11 +1325,11 @@ "@types/node" "*" "@types/node@*": - version "22.10.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.0.tgz#89bfc9e82496b9c7edea3382583fa94f75896e81" - integrity sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA== + version "22.9.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.9.0.tgz#b7f16e5c3384788542c72dc3d561a7ceae2c0365" + integrity sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ== dependencies: - undici-types "~6.20.0" + undici-types "~6.19.8" "@types/node@22.7.5": version "22.7.5" @@ -1339,9 +1344,9 @@ integrity sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ== "@types/node@^20.11.5": - version "20.17.8" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.8.tgz#42748cdb169adf5be7c9760604c72820c7b7d560" - integrity sha512-ahz2g6/oqbKalW9sPv6L2iRbhLnojxjYWspAqhjvqSWBgGebEJT5GvRmk0QXPj3sbC6rU0GTQjPLQkmR8CObvA== + version "20.17.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.6.tgz#6e4073230c180d3579e8c60141f99efdf5df0081" + integrity sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ== dependencies: undici-types "~6.19.2" @@ -1922,9 +1927,9 @@ aws4@^1.2.1, aws4@^1.8.0: integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== axios@^1.4.0: - version "1.7.8" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.8.tgz#1997b1496b394c21953e68c14aaa51b7b5de3d6e" - integrity sha512-Uu0wb7KNqK2t5K+YQyVCLM76prD5sRFjKHbJYCP1J7JFGEQ6nN7HWn9+04LAeiJ3ji54lgS/gZCH1oxyrf1SPw== + version "1.7.7" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" + integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== dependencies: follow-redirects "^1.15.6" form-data "^4.0.0" @@ -2318,9 +2323,9 @@ camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001669: - version "1.0.30001684" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz#0eca437bab7d5f03452ff0ef9de8299be6b08e16" - integrity sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ== + version "1.0.30001680" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz#5380ede637a33b9f9f1fc6045ea99bd142f3da5e" + integrity sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA== capture-stack-trace@^1.0.0: version "1.0.2" @@ -2339,6 +2344,14 @@ chainsaw@~0.1.0: dependencies: traverse ">=0.3.0 <0.4" +chalk@4, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" @@ -2364,14 +2377,6 @@ chalk@^2.0.1: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" @@ -2684,9 +2689,9 @@ cross-spawn@^5.0.1: which "^1.2.9" cross-spawn@^6.0.0: - version "6.0.6" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.6.tgz#30d0efa0712ddb7eb5a76e1e8721bffafa6b5d57" - integrity sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw== + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== dependencies: nice-try "^1.0.4" path-key "^2.0.1" @@ -2695,9 +2700,9 @@ cross-spawn@^6.0.0: which "^1.2.9" cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.6" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" - integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + version "7.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.5.tgz#910aac880ff5243da96b728bc6521a5f6c2f2f82" + integrity sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" @@ -2801,7 +2806,7 @@ debug@^3.1.0, debug@^3.2.7: dependencies: ms "^2.1.1" -debuglog@*, debuglog@^1.0.1: +debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw== @@ -2990,9 +2995,9 @@ ejs@^3.1.10: jake "^10.8.5" electron-to-chromium@^1.5.41: - version "1.5.65" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.65.tgz#e2b9d84d31e187a847e3ccdcfb415ddd4a3d1ea7" - integrity sha512-PWVzBjghx7/wop6n22vS2MLU8tKGd4Q91aCEGhG/TYmW6PP5OcSXcdnxTe1NNt0T66N8D6jxh4kC8UsdzOGaIw== + version "1.5.58" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.58.tgz#d90bd7a04d9223dce4e72316e14492140ec9af40" + integrity sha512-al2l4r+24ZFL7WzyPTlyD0fC33LLzvxqLCwurtBibVPghRGO9hSTl+tis8t1kD7biPiH/en4U0I7o/nQbYeoVA== emittery@^0.13.1: version "0.13.1" @@ -3140,13 +3145,13 @@ es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2: hasown "^2.0.0" es-to-primitive@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" - integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: - is-callable "^1.2.7" - is-date-object "^1.0.5" - is-symbol "^1.0.4" + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" es6-promise@^4.0.3: version "4.2.8" @@ -3650,9 +3655,9 @@ flat-cache@^3.0.4: rimraf "^3.0.2" flatted@^3.2.9: - version "3.3.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.2.tgz#adba1448a9841bec72b42c532ea23dbbedef1a27" - integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA== + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== flush-write-stream@^1.0.0: version "1.1.1" @@ -4217,9 +4222,9 @@ humanize-ms@^1.2.1: ms "^2.0.0" husky@^9.1.6: - version "9.1.7" - resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.7.tgz#d46a38035d101b46a70456a850ff4201344c0b2d" - integrity sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA== + version "9.1.6" + resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.6.tgz#e23aa996b6203ab33534bdc82306b0cf2cb07d6c" + integrity sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A== iconv-lite@0.4.24: version "0.4.24" @@ -4286,7 +4291,7 @@ import-local@^3.0.2: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" -imurmurhash@*, imurmurhash@^0.1.4: +imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== @@ -4397,7 +4402,7 @@ is-builtin-module@^1.0.0: dependencies: builtin-modules "^1.0.0" -is-callable@^1.1.3, is-callable@^1.2.7: +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== @@ -4423,7 +4428,7 @@ is-data-view@^1.0.1: dependencies: is-typed-array "^1.1.13" -is-date-object@^1.0.5: +is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== @@ -4587,7 +4592,7 @@ is-string@^1.0.5, is-string@^1.0.7: dependencies: has-tostringtag "^1.0.0" -is-symbol@^1.0.3, is-symbol@^1.0.4: +is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== @@ -5364,11 +5369,6 @@ lockfile@~1.0.3: dependencies: signal-exit "^3.0.2" -lodash._baseindexof@*: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c" - integrity sha512-bSYo8Pc/f0qAkr8fPJydpJjtrHiSynYfYBjtANIgXv5xEf1WlTC63dIDlgu0s9dmTvzRu1+JJTxcIAHe+sH0FQ== - lodash._baseuniq@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" @@ -5377,33 +5377,11 @@ lodash._baseuniq@~4.6.0: lodash._createset "~4.0.0" lodash._root "~3.0.0" -lodash._bindcallback@*: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" - integrity sha512-2wlI0JRAGX8WEf4Gm1p/mv/SZ+jLijpj0jyaE/AXeuQphzCgD8ZQW4oSpoN8JAopujOFGU3KMuq7qfHBWlGpjQ== - -lodash._cacheindexof@*: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92" - integrity sha512-S8dUjWr7SUT/X6TBIQ/OYoCHo1Stu1ZRy6uMUSKqzFnZp5G5RyQizSm6kvxD2Ewyy6AVfMg4AToeZzKfF99T5w== - -lodash._createcache@*: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093" - integrity sha512-ev5SP+iFpZOugyab/DEUQxUeZP5qyciVTlgQ1f4Vlw7VUcCD8fVnyIqVUEIaoFH9zjAqdgi69KiofzvVmda/ZQ== - dependencies: - lodash._getnative "^3.0.0" - lodash._createset@~4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" integrity sha512-GTkC6YMprrJZCYU3zcqZj+jkXkrXzq3IPBcF/fIPpNEAB4hZEtXU8zp/RwKOvZl43NUmwDbyRk3+ZTbeRdEBXA== -lodash._getnative@*, lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" - integrity sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA== - lodash._root@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" @@ -5479,11 +5457,6 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.restparam@*: - version "3.6.1" - resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" - integrity sha512-L4/arjjuq4noiUJpt3yS6KIKDtJwNe2fIYgMqyYYKoeIfV1iEqvPwhCx23o+R9dzouGihDAPN1dTIRWa7zk8tw== - lodash.truncate@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" @@ -5767,9 +5740,9 @@ mongodb@5.9.2: "@mongodb-js/saslprep" "^1.1.0" mongoose@^7.4.2: - version "7.8.3" - resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-7.8.3.tgz#d6d3305268fc93a425c4a5f41478b0c1771a5b1c" - integrity sha512-eFnbkKgyVrICoHB6tVJ4uLanS7d5AIo/xHkEbQeOv6g2sD7gh/1biRwvFifsmbtkIddQVNr3ROqHik6gkknN3g== + version "7.8.2" + resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-7.8.2.tgz#578e4d0b0b60421459399cfc47cab2a43d90155f" + integrity sha512-/KDcZL84gg8hnmOHRRPK49WtxH3Xsph38c7YqvYPdxEB2OsDAXvwAknGxyEC0F2P3RJCqFOp+523iFCa0p3dfw== dependencies: bson "^5.5.0" kareem "2.5.1" @@ -6688,9 +6661,9 @@ pseudomap@^1.0.2: integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== psl@^1.1.28: - version "1.13.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.13.0.tgz#8b2357f13ef3cf546af3f52de00543a94da86cfa" - integrity sha512-BFwmFXiJoFqlUpZ5Qssolv15DMyc84gTBds1BjsV1BfXEo1UyyD7GsmN67n7J77uRhoSNW1AXtXKPLcBFQn9Aw== + version "1.10.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.10.0.tgz#1450f7e16f922c3beeb7bd9db3f312635018fa15" + integrity sha512-KSKHEbjAnpUuAUserOq0FxGXCUrzC3WniuSJhvdbs102rL55266ZcHBqLWOsG30spQMlPdpy7icATiAQehg/iA== dependencies: punycode "^2.3.1" @@ -6894,7 +6867,7 @@ readdir-glob@^1.1.2: dependencies: minimatch "^5.1.0" -readdir-scoped-modules@*, readdir-scoped-modules@^1.0.0: +readdir-scoped-modules@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== @@ -7918,9 +7891,9 @@ tr46@^3.0.0: integrity sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ== ts-api-utils@^1.0.1: - version "1.4.2" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.2.tgz#a6a6dff26117ac7965624fc118525971edc6a82a" - integrity sha512-ZF5gQIQa/UmzfvxbHZI3JXN0/Jt+vnAfAviNRAMc491laiK6YCLpCW9ft8oaCRFOTxCZtUTE6XB0ZQAe3olntw== + version "1.4.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.0.tgz#709c6f2076e511a81557f3d07a0cbd566ae8195c" + integrity sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ== ts-jest@^29.1.1: version "29.2.5" @@ -8089,16 +8062,11 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -undici-types@~6.19.2: +undici-types@~6.19.2, undici-types@~6.19.8: version "6.19.8" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== -undici-types@~6.20.0: - version "6.20.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" - integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== - unique-filename@^1.1.0, unique-filename@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" @@ -8242,7 +8210,7 @@ v8-to-istanbul@^9.0.1: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^2.0.0" -validate-npm-package-license@*, validate-npm-package-license@^3.0.1: +validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==