├── .gitignore ├── .prettierignore ├── .rustfmt.toml ├── .vscode └── launch.json ├── Anchor.toml ├── Cargo.lock ├── Cargo.toml ├── LICENCE ├── README.md ├── migrations └── deploy.ts ├── package-lock.json ├── package.json ├── programs └── deal_contract │ ├── Cargo.toml │ ├── Xargo.toml │ └── src │ ├── constants.rs │ ├── errors.rs │ ├── instructions │ ├── cancel.rs │ ├── finish.rs │ ├── initialize.rs │ ├── mod.rs │ ├── partially_pay.rs │ └── update_checker.rs │ ├── lib.rs │ ├── state │ └── mod.rs │ └── utils │ ├── checklist.rs │ └── mod.rs ├── tests ├── client.ts ├── deal_contract.ts └── keys │ ├── alt.json │ ├── checker.json │ ├── client.json │ ├── deal_contract.json │ ├── executor.json │ ├── holder_mint.json │ ├── index.ts │ ├── mint_authority.json │ ├── payer.json │ ├── service.json │ ├── service_fee_mint.json │ └── upgrade_authority.json ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .anchor 3 | .DS_Store 4 | target 5 | **/*.rs.bk 6 | node_modules 7 | test-ledger 8 | .env -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | 2 | .anchor 3 | .DS_Store 4 | target 5 | node_modules 6 | dist 7 | build 8 | test-ledger 9 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 100 2 | hard_tabs = false 3 | tab_spaces = 4 4 | newline_style = "Auto" 5 | indent_style = "Block" 6 | use_small_heuristics = "Default" 7 | fn_call_width = 75 8 | attr_fn_like_width = 70 9 | struct_lit_width = 18 10 | struct_variant_width = 45 11 | array_width = 70 12 | chain_width = 80 13 | single_line_if_else_max_width = 75 14 | wrap_comments = false 15 | format_code_in_doc_comments = false 16 | doc_comment_code_block_width = 100 17 | comment_width = 80 18 | normalize_comments = false 19 | normalize_doc_attributes = false 20 | format_strings = false 21 | format_macro_matchers = false 22 | format_macro_bodies = true 23 | hex_literal_case = "Preserve" 24 | empty_item_single_line = true 25 | struct_lit_single_line = true 26 | fn_single_line = false 27 | where_single_line = false 28 | imports_indent = "Block" 29 | imports_layout = "Mixed" 30 | imports_granularity = "Preserve" 31 | group_imports = "Preserve" 32 | reorder_imports = true 33 | reorder_modules = true 34 | reorder_impl_items = false 35 | type_punctuation_density = "Wide" 36 | space_before_colon = false 37 | space_after_colon = true 38 | spaces_around_ranges = false 39 | binop_separator = "Front" 40 | remove_nested_parens = true 41 | combine_control_expr = true 42 | short_array_element_width_threshold = 10 43 | overflow_delimited_expr = false 44 | struct_field_align_threshold = 0 45 | enum_discrim_align_threshold = 0 46 | match_arm_blocks = true 47 | match_arm_leading_pipes = "Never" 48 | force_multiline_blocks = false 49 | fn_args_layout = "Tall" 50 | brace_style = "SameLineWhere" 51 | control_brace_style = "AlwaysSameLine" 52 | trailing_semicolon = true 53 | trailing_comma = "Vertical" 54 | match_block_trailing_comma = false 55 | blank_lines_upper_bound = 1 56 | blank_lines_lower_bound = 0 57 | edition = "2015" 58 | version = "One" 59 | inline_attribute_width = 0 60 | format_generated_files = true 61 | merge_derives = true 62 | use_try_shorthand = false 63 | use_field_init_shorthand = false 64 | force_explicit_abi = true 65 | condense_wildcard_suffixes = false 66 | color = "Auto" 67 | required_version = "1.5.1" 68 | unstable_features = false 69 | disable_all_formatting = false 70 | skip_children = false 71 | hide_parse_errors = false 72 | error_on_line_overflow = false 73 | error_on_unformatted = false 74 | ignore = [] 75 | emit_mode = "Files" 76 | make_backup = false 77 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "command": "anchor test", 6 | "name": "Run anchor test", 7 | "request": "launch", 8 | "type": "node-terminal", 9 | "env": { 10 | "MINT_KEY_PATH": "./.env/key.json", 11 | "TOKEN_MINT_KEY_PATH": "./.env/token.json", 12 | } 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /Anchor.toml: -------------------------------------------------------------------------------- 1 | [features] 2 | seeds = false 3 | skip-lint = false 4 | [programs.devnet] 5 | deal_contract = "9kpWdyR2qtNT21MhLRTBbT21v5thz9hhB3zaPUhr6tbE" 6 | [programs.localnet] 7 | deal_contract = "GKNkN4uDJWmidEC9h5Q9GQXNg48Go6q5bdnkDj6bSopz" 8 | 9 | [registry] 10 | url = "https://api.apr.dev" 11 | 12 | [provider] 13 | cluster = "mainnet" 14 | wallet = "~/solana/upgrade_authority.json" 15 | 16 | [test] 17 | startup_wait = 10000 18 | 19 | [[test.genesis]] 20 | address = "GKNkN4uDJWmidEC9h5Q9GQXNg48Go6q5bdnkDj6bSopz" 21 | program = "target/deploy/deal_contract.so" 22 | 23 | [test.validator] 24 | url = "https://api.mainnet-beta.solana.com" # This is the url of the cluster that accounts are cloned from (See `test.validator.clone`). 25 | 26 | [[test.validator.clone]] 27 | address = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" 28 | [[test.validator.clone]] 29 | address = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" 30 | [[test.validator.account]] 31 | address = "9479m8V6EuvFKPC812s8BZ3g3hD5Ru63hkR23Y7DLvEs" 32 | filename = "tests/keys/alt.json" 33 | [[test.validator.mint]] 34 | address = "2w1cBfq2PNPCk8KjK2j9oaMJjZUa4pkSNTCrfeB7cYRt" 35 | 36 | [scripts] 37 | test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" 38 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "programs/*", 4 | ] 5 | 6 | [profile.release] 7 | overflow-checks = true 8 | lto = "fat" 9 | codegen-units = 1 10 | [profile.release.build-override] 11 | opt-level = 3 12 | incremental = false 13 | codegen-units = 1 14 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 HashHorizon Technologies 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Contractus - Solana Smart-Contract 2 | This repository contains the source code of the Solana smart-contract for the [`Contractus`](http://contractus.tech/) service. -------------------------------------------------------------------------------- /migrations/deploy.ts: -------------------------------------------------------------------------------- 1 | // Migrations are an early feature. Currently, they're nothing more than this 2 | // single deploy script that's invoked from the CLI, injecting a provider 3 | // configured from the workspace's Anchor.toml. 4 | 5 | const anchor = require("@project-serum/anchor"); 6 | 7 | module.exports = async function (provider) { 8 | // Configure client to use the provider. 9 | anchor.setProvider(provider); 10 | 11 | // Add your deploy script here. 12 | }; 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w", 4 | "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check" 5 | }, 6 | "dependencies": { 7 | "@coral-xyz/anchor": "^0.28.0", 8 | "@solana/spl-token": "^0.3.5", 9 | "uuid": "^9.0.0" 10 | }, 11 | "devDependencies": { 12 | "@types/bn.js": "^5.1.0", 13 | "@types/chai": "^4.3.5", 14 | "@types/mocha": "^10.0.1", 15 | "chai": "^4.3.7", 16 | "mocha": "^10.2.0", 17 | "prettier": "^2.6.2", 18 | "ts-mocha": "^10.0.0", 19 | "typescript": "^5.1.6" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /programs/deal_contract/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deal_contract" 3 | version = "0.1.0" 4 | description = "Created with Anchor" 5 | edition = "2021" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "lib"] 9 | name = "deal_contract" 10 | 11 | [features] 12 | no-entrypoint = [] 13 | no-idl = [] 14 | no-log-ix-name = [] 15 | cpi = ["no-entrypoint"] 16 | default = [] 17 | 18 | [dependencies] 19 | anchor-lang = {version = "0.28.0", features = ["init-if-needed"]} 20 | anchor-spl = "0.28.0" 21 | solana-program = "1.15.2" 22 | spl-associated-token-account = { version = "1.1.3", features = ["no-entrypoint"] } 23 | # spl-token = {version = "3.3.0", features = ["no-entrypoint"]} 24 | -------------------------------------------------------------------------------- /programs/deal_contract/Xargo.toml: -------------------------------------------------------------------------------- 1 | [target.bpfel-unknown-unknown.dependencies.std] 2 | features = [] 3 | -------------------------------------------------------------------------------- /programs/deal_contract/src/constants.rs: -------------------------------------------------------------------------------- 1 | use anchor_lang::prelude::*; 2 | use solana_program::pubkey; 3 | 4 | pub const DEAL_STATE_SEED: &[u8] = b"deal_state"; 5 | 6 | // NEED CHECK: CTUS address 7 | // Devnet: CyhjLfsfDz7rtszqBGaHiFrBbck2LNKEXQkywqNrGVyw 8 | // Mainnet: --- 9 | // Localnet: 67sJHNFLxkREsdu35n8tmfudv1ZU59XhBYjc6rivMt2V 10 | pub const SERVICE_FEE_MINT: Pubkey = pubkey!("62PtWFh2dQ69LKbHumBpMa7wG71r7i7Damwo2wMYfcR1"); 11 | // pub const SERVICE_FEE_TA: Pubkey = pubkey!("11111111111111111111111111111111"); 12 | pub const SERVICE_FEE_OWNER: Pubkey = pubkey!("C8pHACh7SAZWVZvgppM6VkWEDC6voLGGctch5Vr5hkEz"); 13 | 14 | // NEED CHECK: Admin level 15 | // Devnet: 3aDaxu2XwsGmj7amUnrxaHoKTtKJUqebkYP9HJTkP434 16 | // Mainnet: --- 17 | // Localnet: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA 18 | pub const SERVICE_ACCOUNT_ADDRESS: Pubkey = pubkey!("C8pHACh7SAZWVZvgppM6VkWEDC6voLGGctch5Vr5hkEz"); 19 | 20 | // NEED CHECK: 10_000 CTUS 21 | #[constant] 22 | pub const HOLDER_MODE_AMOUNT: u64 = 10000000000000; 23 | 24 | pub const HOLDER_MINT: Pubkey = pubkey!("64esx9p99rgwzmBCFCaUDCKJL2b2WrgdFe7chyaDyrKD"); 25 | -------------------------------------------------------------------------------- /programs/deal_contract/src/errors.rs: -------------------------------------------------------------------------------- 1 | use anchor_lang::prelude::*; 2 | 3 | #[error_code] 4 | pub enum ErrorCodes { 5 | #[msg("The fee is too low")] 6 | FeeIsTooLow, 7 | #[msg("Holder mode unavailable")] 8 | HolderModeUnavailable, 9 | #[msg("The amount is too small.")] 10 | AmountTooLow, 11 | #[msg("The deadline has not yet come.")] 12 | DeadlineNotExpired, 13 | #[msg("Deadline expired")] 14 | DeadlineExpired, 15 | #[msg("Not implemented method")] 16 | NotImplemented, 17 | 18 | #[msg("DealStateNotWithChecker")] 19 | DealStateNotWithChecker, 20 | #[msg("DealStateWithChecker")] 21 | DealStateWithChecker, 22 | 23 | #[msg("NoClientBond")] 24 | NoClientBond, 25 | #[msg("NoExecutorBond")] 26 | NoExecutorBond, 27 | 28 | #[msg("InvalidMint")] 29 | InvalidMint, 30 | #[msg("InvalidOwner")] 31 | InvalidOwner, 32 | 33 | #[msg("DealWithClientBond")] 34 | DealWithClientBond, 35 | 36 | #[msg("DealWithExecutorBond")] 37 | DealWithExecutorBond, 38 | } 39 | 40 | #[error_code] 41 | pub enum InvalidAccount { 42 | #[msg("Initializer")] 43 | Initializer, 44 | #[msg("Checker")] 45 | Checker, 46 | #[msg("CheckerDealTokenAccount")] 47 | CheckerDealTokenAccount, 48 | 49 | #[msg("ClientBondMint")] 50 | ClientBondMint, 51 | #[msg("ExecutorBondMint")] 52 | ExecutorBondMint, 53 | #[msg("DealStateClientBondMint")] 54 | DealStateClientBondMint, 55 | #[msg("DealStateExecutorBondMint")] 56 | DealStateExecutorBondMint, 57 | #[msg("DealStateHolderTokenAccount")] 58 | DealStateHolderTokenAccount, 59 | 60 | #[msg("ClientBondTokenAccount")] 61 | ClientBondTokenAccount, 62 | #[msg("ExecutorBondTokenAccount")] 63 | ExecutorBondTokenAccount, 64 | 65 | #[msg("DealStateClientBondTokenAccount")] 66 | DealStateClientBondTokenAccount, 67 | #[msg("DealStateExecutorBondTokenAccount")] 68 | DealStateExecutorBondTokenAccount, 69 | 70 | #[msg("ClientHolderTokenAccount")] 71 | ClientHolderTokenAccount, 72 | #[msg("ClientHolderTokenAccountMint")] 73 | ClientHolderTokenAccountMint, 74 | 75 | #[msg("ClientHolderTokenAccountOwner")] 76 | ClientHolderTokenAccountOwner, 77 | } 78 | -------------------------------------------------------------------------------- /programs/deal_contract/src/instructions/cancel.rs: -------------------------------------------------------------------------------- 1 | use anchor_lang::prelude::*; 2 | use anchor_spl::{ 3 | associated_token::AssociatedToken, 4 | token::{self, CloseAccount, Mint, Token, TokenAccount, Transfer}, 5 | token_interface::spl_token_2022::cmp_pubkeys, 6 | }; 7 | 8 | use crate::{ 9 | constants::*, 10 | errors::{ErrorCodes, InvalidAccount}, 11 | state::{Bond, Checker, DealState}, 12 | utils::{ 13 | check_ta, init_ata, AccountClosed, BondsTransfered, CheckerFeeTransfered, DeadlineChecked, 14 | DepositTransfered, 15 | }, 16 | }; 17 | 18 | #[derive(Accounts)] 19 | pub struct Cancel<'info> { 20 | /// CHECK: check is performed in access_control 21 | #[account(mut, signer)] 22 | pub initializer: AccountInfo<'info>, 23 | /// CHECK: check is performed in access_control 24 | pub checker: AccountInfo<'info>, 25 | /// CHECK: 26 | #[account(address = deal_state.client_key)] 27 | pub client: AccountInfo<'info>, 28 | /// CHECK: 29 | #[account(address = deal_state.executor_key)] 30 | pub executor: AccountInfo<'info>, 31 | /// CHECK: 32 | #[account(mut, signer)] 33 | pub payer: AccountInfo<'info>, 34 | 35 | #[account(mut, 36 | constraint = cmp_pubkeys(&deal_state.deal_token_mint, &deal_state_deal_ta.mint), 37 | constraint = cmp_pubkeys(&deal_state_deal_ta.owner, &deal_state.key()) 38 | )] 39 | pub deal_state_deal_ta: Box>, 40 | #[account(init_if_needed, payer = payer, 41 | associated_token::mint = deal_mint, 42 | associated_token::authority = client, 43 | )] 44 | pub client_deal_ta: Box>, 45 | 46 | /// CHECK: in access_control. may be uninitialized. 47 | #[account(mut)] 48 | pub checker_deal_ta: AccountInfo<'info>, 49 | 50 | /// CHECK: in access_control. may be uninitialized. 51 | #[account(mut)] 52 | pub client_bond_ta: AccountInfo<'info>, 53 | /// CHECK: in access_control. may be uninitialized. 54 | #[account(mut)] 55 | pub executor_bond_ta: AccountInfo<'info>, 56 | 57 | /// CHECK: in access_control 58 | #[account(mut)] 59 | pub deal_state_client_bond_ta: AccountInfo<'info>, 60 | /// CHECK: in access_control 61 | #[account(mut)] 62 | pub deal_state_executor_bond_ta: AccountInfo<'info>, 63 | 64 | #[account(constraint = cmp_pubkeys(&deal_mint.key(), &deal_state.deal_token_mint))] 65 | pub deal_mint: Box>, 66 | /// CHECK: in transfer_bonds 67 | pub client_bond_mint: AccountInfo<'info>, 68 | /// CHECK: in transfer_bonds 69 | pub executor_bond_mint: AccountInfo<'info>, 70 | 71 | /// CHECK: constant address 72 | #[account(mut, address = SERVICE_FEE_OWNER)] 73 | pub service_fee: AccountInfo<'info>, 74 | #[account(mut, close = initializer)] 75 | pub deal_state: Box>, 76 | pub associated_token_program: Program<'info, AssociatedToken>, 77 | pub token_program: Program<'info, Token>, 78 | pub system_program: Program<'info, System>, 79 | } 80 | 81 | enum Initializer { 82 | Client, 83 | Executor, 84 | Checker, 85 | Service, 86 | } 87 | 88 | impl<'info> TryFrom<&Cancel<'info>> for Initializer { 89 | type Error = anchor_lang::error::Error; 90 | fn try_from(value: &Cancel<'info>) -> Result { 91 | let initializer_key = &value.initializer.key; 92 | if cmp_pubkeys(initializer_key, value.checker.key) { 93 | Ok(Initializer::Checker) 94 | } else if cmp_pubkeys(&initializer_key, value.client.key) { 95 | Ok(Initializer::Client) 96 | } else if cmp_pubkeys(&initializer_key, value.executor.key) { 97 | Ok(Initializer::Executor) 98 | } else if cmp_pubkeys(&initializer_key, &SERVICE_ACCOUNT_ADDRESS) { 99 | Ok(Initializer::Service) 100 | } else { 101 | Err(InvalidAccount::Initializer.into()) 102 | } 103 | } 104 | } 105 | 106 | #[allow(dead_code)] 107 | struct Checklist { 108 | deadline_checked: DeadlineChecked, 109 | checker_fee_transfered: CheckerFeeTransfered, 110 | deposit_transfered: DepositTransfered, 111 | bonds_transfered: BondsTransfered, 112 | deal_state_deal_ta_closed: AccountClosed, 113 | } 114 | 115 | impl<'info> Cancel<'info> { 116 | fn check_accounts(ctx: &Context) -> Result<()> { 117 | if let Some(Checker { checker_key, .. }) = ctx.accounts.deal_state.checker.as_ref() { 118 | if !cmp_pubkeys(ctx.accounts.checker.as_ref().key, &checker_key) { 119 | return Err(InvalidAccount::Checker.into()); 120 | }; 121 | 122 | match Account::::try_from(&ctx.accounts.checker_deal_ta) { 123 | Ok(checker_deal_ta) => { 124 | check_ta( 125 | &checker_deal_ta, 126 | ctx.accounts.deal_mint.to_account_info().key, 127 | ctx.accounts.checker.key, 128 | ) 129 | .map_err(|_| InvalidAccount::CheckerDealTokenAccount)?; 130 | } 131 | Err(_) => { 132 | init_ata( 133 | &ctx.accounts.payer, 134 | &ctx.accounts.deal_mint.to_account_info(), 135 | &ctx.accounts.checker, 136 | &ctx.accounts.checker_deal_ta, 137 | &ctx.accounts.token_program, 138 | )?; 139 | } 140 | }; 141 | } 142 | 143 | if let Some(Bond { mint, .. }) = ctx.accounts.deal_state.client_bond.as_ref() { 144 | if !cmp_pubkeys(&mint, &ctx.accounts.client_bond_mint.key) { 145 | return Err(InvalidAccount::ClientBondMint)?; 146 | } 147 | 148 | let deal_state_client_bond_ta = 149 | Account::::try_from(&ctx.accounts.deal_state_client_bond_ta)?; 150 | match Account::::try_from(&ctx.accounts.client_bond_ta) { 151 | Ok(client_bond_ta) => { 152 | check_ta( 153 | &client_bond_ta, 154 | &ctx.accounts.client_bond_mint.key(), 155 | ctx.accounts.client.key, 156 | ) 157 | .map_err(|_| InvalidAccount::ClientBondTokenAccount)?; 158 | check_ta( 159 | &deal_state_client_bond_ta, 160 | &ctx.accounts.client_bond_mint.key(), 161 | ctx.accounts.deal_state.to_account_info().key, 162 | ) 163 | .map_err(|_| InvalidAccount::DealStateClientBondTokenAccount)?; 164 | } 165 | Err(_) => { 166 | init_ata( 167 | &ctx.accounts.payer, 168 | &ctx.accounts.client_bond_mint.to_account_info(), 169 | &ctx.accounts.client, 170 | &ctx.accounts.client_bond_ta, 171 | &ctx.accounts.token_program, 172 | ) 173 | .map_err(|_| InvalidAccount::ClientBondTokenAccount)?; 174 | } 175 | }; 176 | }; 177 | 178 | if let Some(Bond { mint, .. }) = ctx.accounts.deal_state.executor_bond.as_ref() { 179 | if !cmp_pubkeys(&mint, &ctx.accounts.executor_bond_mint.key) { 180 | return Err(InvalidAccount::ExecutorBondMint)?; 181 | } 182 | 183 | let deal_state_executor_bond_ta = 184 | Account::::try_from(&ctx.accounts.deal_state_executor_bond_ta)?; 185 | match Account::::try_from(&ctx.accounts.executor_bond_ta) { 186 | Ok(executor_bond_ta) => { 187 | check_ta( 188 | &executor_bond_ta, 189 | &ctx.accounts.executor_bond_mint.key(), 190 | ctx.accounts.executor.key, 191 | ) 192 | .map_err(|_| InvalidAccount::ExecutorBondTokenAccount)?; 193 | check_ta( 194 | &deal_state_executor_bond_ta, 195 | &ctx.accounts.executor_bond_mint.key(), 196 | ctx.accounts.deal_state.to_account_info().key, 197 | ) 198 | .map_err(|_| InvalidAccount::DealStateExecutorBondTokenAccount)?; 199 | } 200 | Err(_) => { 201 | init_ata( 202 | &ctx.accounts.payer, 203 | &ctx.accounts.executor_bond_mint.to_account_info(), 204 | &ctx.accounts.executor, 205 | &ctx.accounts.executor_bond_ta, 206 | &ctx.accounts.token_program, 207 | ) 208 | .map_err(|_| InvalidAccount::ExecutorBondTokenAccount)?; 209 | } 210 | }; 211 | }; 212 | 213 | Ok(()) 214 | } 215 | 216 | fn check_deadline(&self) -> Result { 217 | if self.deal_state.deadline_ts.is_some() && !self.deal_state.deadline_expired() { 218 | return Err(ErrorCodes::DeadlineNotExpired.into()); 219 | } 220 | Ok(DeadlineChecked) 221 | } 222 | 223 | fn transfer_checker_fee(&self) -> Result { 224 | if let Some(Checker { checker_fee, .. }) = self.deal_state.checker { 225 | token::transfer( 226 | CpiContext::new_with_signer( 227 | self.token_program.to_account_info(), 228 | Transfer { 229 | from: self.deal_state_deal_ta.to_account_info(), 230 | to: self.checker_deal_ta.to_account_info(), 231 | authority: self.deal_state.to_account_info(), 232 | }, 233 | &[&self.deal_state.seeds()[..]], 234 | ), 235 | checker_fee, 236 | )?; 237 | }; 238 | Ok(CheckerFeeTransfered) 239 | } 240 | 241 | fn transfer_deposit(&self) -> Result { 242 | let amount_to_transfer = self.deal_state.amount.saturating_sub(self.deal_state.paid_amount); 243 | if amount_to_transfer > 0 { 244 | token::transfer( 245 | CpiContext::new_with_signer( 246 | self.token_program.to_account_info(), 247 | Transfer { 248 | from: self.deal_state_deal_ta.to_account_info(), 249 | to: self.client_deal_ta.to_account_info(), 250 | authority: self.deal_state.to_account_info(), 251 | }, 252 | &[&self.deal_state.seeds()], 253 | ), 254 | amount_to_transfer, 255 | )?; 256 | } 257 | Ok(DepositTransfered) 258 | } 259 | 260 | fn transfer_bonds(&mut self, initializer: Initializer) -> Result { 261 | if let Some(Bond { amount, .. }) = self.deal_state.client_bond.as_ref() { 262 | if let Initializer::Executor = initializer { 263 | return Err(ErrorCodes::DealWithClientBond.into()); 264 | } 265 | if *amount > 0 { 266 | anchor_spl::token::transfer( 267 | CpiContext::new( 268 | self.token_program.to_account_info(), 269 | token::Transfer { 270 | from: self.deal_state_client_bond_ta.to_account_info(), 271 | to: self.client_bond_ta.to_account_info(), 272 | authority: self.deal_state.to_account_info(), 273 | }, 274 | ) 275 | .with_signer(&[&self.deal_state.seeds()[..]]), 276 | *amount, 277 | )?; 278 | } 279 | } 280 | if let Some(Bond { amount, .. }) = self.deal_state.executor_bond.as_ref() { 281 | if let Initializer::Client = initializer { 282 | return Err(ErrorCodes::DealWithExecutorBond.into()); 283 | } 284 | if *amount > 0 { 285 | anchor_spl::token::transfer( 286 | CpiContext::new( 287 | self.token_program.to_account_info(), 288 | token::Transfer { 289 | from: self.deal_state_client_bond_ta.to_account_info(), 290 | to: self.executor_bond_ta.to_account_info(), 291 | authority: self.deal_state.to_account_info(), 292 | }, 293 | ) 294 | .with_signer(&[&self.deal_state.seeds()[..]]), 295 | *amount, 296 | )?; 297 | } 298 | } 299 | 300 | if self.deal_state.client_bond.is_some() { 301 | self.close_deal_state_ta(&self.deal_state_client_bond_ta.clone())?; 302 | } 303 | if self.deal_state.executor_bond.is_some() 304 | && !cmp_pubkeys( 305 | self.deal_state_client_bond_ta.key, 306 | self.deal_state_executor_bond_ta.key, 307 | ) 308 | { 309 | self.close_deal_state_ta(&self.deal_state_executor_bond_ta.clone())?; 310 | } 311 | 312 | Ok(BondsTransfered) 313 | } 314 | 315 | fn close_deal_state_ta(&self, token_account: &AccountInfo<'info>) -> Result { 316 | token::close_account(CpiContext::new_with_signer( 317 | self.token_program.to_account_info(), 318 | CloseAccount { 319 | account: token_account.clone(), 320 | destination: self.service_fee.to_account_info(), 321 | authority: self.deal_state.to_account_info(), 322 | }, 323 | &[&self.deal_state.seeds()[..]], 324 | ))?; 325 | Ok(AccountClosed) 326 | } 327 | } 328 | 329 | #[access_control(Cancel::check_accounts(&ctx))] 330 | pub fn handle(ctx: Context) -> Result<()> { 331 | let initializer = Initializer::try_from(&*ctx.accounts)?; 332 | let deadline_checked = ctx.accounts.check_deadline()?; 333 | let checker_fee_transfered = ctx.accounts.transfer_checker_fee()?; 334 | let deposit_transfered = ctx.accounts.transfer_deposit()?; 335 | let bonds_transfered = ctx.accounts.transfer_bonds(initializer)?; 336 | 337 | let deal_state_deal_ta_closed = if ctx.accounts.deal_state_deal_ta.to_account_info().lamports() 338 | == 0 339 | { 340 | AccountClosed 341 | } else { 342 | ctx.accounts 343 | .close_deal_state_ta(AsRef::::as_ref(&*ctx.accounts.deal_state_deal_ta))? 344 | }; 345 | 346 | Checklist { 347 | deadline_checked, 348 | checker_fee_transfered, 349 | deposit_transfered, 350 | bonds_transfered, 351 | deal_state_deal_ta_closed, 352 | }; 353 | 354 | Ok(()) 355 | } 356 | -------------------------------------------------------------------------------- /programs/deal_contract/src/instructions/finish.rs: -------------------------------------------------------------------------------- 1 | use anchor_lang::prelude::*; 2 | use anchor_spl::{token::{self, CloseAccount, Token, TokenAccount, Transfer, Mint}, token_interface::spl_token_2022::cmp_pubkeys, associated_token::AssociatedToken}; 3 | 4 | use crate::{constants::*, state::{DealState, Checker, Bond}, 5 | utils::{CheckerFeeTransfered, PaymentTransfered, BondsTransfered, AccountClosed, init_ata, check_ta, HolderModeHandled}, errors::InvalidAccount}; 6 | 7 | #[derive(Accounts)] 8 | pub struct Finish<'info> { 9 | /// CHECK: 10 | #[account(mut, signer, constraint = 11 | cmp_pubkeys(&initializer.key, checker.key) 12 | || cmp_pubkeys(&initializer.key, client.key) 13 | )] 14 | pub initializer: AccountInfo<'info>, 15 | /// CHECK: 16 | #[account(address = deal_state.client_key)] 17 | pub client: AccountInfo<'info>, 18 | /// CHECK: 19 | #[account(address = deal_state.executor_key)] 20 | pub executor: AccountInfo<'info>, 21 | /// CHECK: check in access_control 22 | pub checker: AccountInfo<'info>, 23 | /// CHECK: 24 | #[account(mut, signer)] 25 | pub payer: AccountInfo<'info>, 26 | 27 | #[account(mut, 28 | constraint = cmp_pubkeys(&deal_state.deal_token_mint, &deal_state_deal_ta.mint), 29 | constraint = cmp_pubkeys(&deal_state_deal_ta.owner, &deal_state.key()) 30 | )] 31 | pub deal_state_deal_ta: Box>, 32 | /// CHECK: may be uninitialized. check in access_control 33 | #[account(mut)] 34 | pub deal_state_holder_ta: AccountInfo<'info>, 35 | /// CHECK: may be uninitialized. check in access_control 36 | #[account(mut)] 37 | pub client_holder_ta: AccountInfo<'info>, 38 | #[account(init_if_needed, payer = initializer, 39 | associated_token::mint = deal_mint, 40 | associated_token::authority = executor, 41 | )] 42 | pub executor_deal_ta: Box>, 43 | /// CHECK: may be uninitialized. check in access_control 44 | #[account(mut)] 45 | pub checker_deal_ta: AccountInfo<'info>, 46 | 47 | /// CHECK: may be uninitialized. check in access_control 48 | #[account(mut)] 49 | pub deal_state_client_bond_ta: AccountInfo<'info>, 50 | /// CHECK: may be uninitialized. check in access_control 51 | #[account(mut)] 52 | pub deal_state_executor_bond_ta: AccountInfo<'info>, 53 | /// CHECK: may be uninitialized. check in access_control 54 | #[account(mut)] 55 | pub client_bond_ta: AccountInfo<'info>, 56 | /// CHECK: may be uninitialized. check in access_control 57 | #[account(mut)] 58 | pub executor_bond_ta: AccountInfo<'info>, 59 | 60 | #[account(address = deal_state.deal_token_mint)] 61 | pub deal_mint: Box>, 62 | #[account(address = HOLDER_MINT)] 63 | pub holder_mint: Box>, 64 | pub client_bond_mint: Box>, 65 | pub executor_bond_mint: Box>, 66 | 67 | /// CHECK: constant address 68 | #[account(mut, address = SERVICE_FEE_OWNER)] 69 | pub service_fee: AccountInfo<'info>, 70 | #[account(mut, constraint = 71 | cmp_pubkeys(initializer.to_account_info().key, &deal_state.client_key) 72 | || if let Some(Checker{checker_key, ..}) = deal_state.checker.as_ref() { 73 | cmp_pubkeys(initializer.to_account_info().key, &checker_key)} else { true }, 74 | close = service_fee 75 | )] 76 | pub deal_state: Box>, 77 | pub associated_token_program: Program<'info, AssociatedToken>, 78 | pub token_program: Program<'info, Token>, 79 | pub system_program: Program<'info, System>, 80 | } 81 | 82 | impl<'info> Finish<'info> { 83 | fn check_accounts(ctx: &Context) -> Result<()> { 84 | if ctx.accounts.deal_state.holder_mode.is_some() { 85 | let deal_state_holder_ta = Account::::try_from(&ctx.accounts.deal_state_holder_ta) 86 | .map_err(|_|InvalidAccount::DealStateHolderTokenAccount)?; 87 | if !cmp_pubkeys(&deal_state_holder_ta.owner, ctx.accounts.deal_state.to_account_info().key) 88 | || !cmp_pubkeys(&deal_state_holder_ta.mint, &HOLDER_MINT){ 89 | return Err(InvalidAccount::DealStateHolderTokenAccount.into()) 90 | } 91 | 92 | match Account::::try_from(&ctx.accounts.client_holder_ta) { 93 | Ok(client_holder_ta) => { 94 | if !cmp_pubkeys(&client_holder_ta.owner, ctx.accounts.client.to_account_info().key) 95 | || !cmp_pubkeys(&client_holder_ta.mint, &HOLDER_MINT){ 96 | return Err(InvalidAccount::DealStateHolderTokenAccount.into()) 97 | } 98 | }, 99 | Err(_) => { 100 | init_ata( 101 | &ctx.accounts.payer, 102 | &ctx.accounts.holder_mint.to_account_info(), 103 | &ctx.accounts.client.to_account_info(), 104 | &ctx.accounts.client_holder_ta.to_account_info(), 105 | &ctx.accounts.token_program.to_account_info() 106 | )?; 107 | } 108 | } 109 | } 110 | 111 | if let Some(Checker{checker_key, ..}) = ctx.accounts.deal_state.checker.as_ref() { 112 | match Account::::try_from(&ctx.accounts.checker_deal_ta) { 113 | Ok(checker_deal_ta) => { 114 | if !cmp_pubkeys(ctx.accounts.checker.as_ref().key, &checker_key) { 115 | return Err(InvalidAccount::Checker.into()) 116 | }; 117 | if !cmp_pubkeys(&checker_deal_ta.owner, &checker_key) 118 | || !cmp_pubkeys(&checker_deal_ta.mint, &ctx.accounts.deal_mint.key()) { 119 | return Err(InvalidAccount::CheckerDealTokenAccount.into()) 120 | }; 121 | }, 122 | Err(_) => { 123 | init_ata( 124 | &ctx.accounts.payer, 125 | &ctx.accounts.deal_mint.to_account_info(), 126 | &ctx.accounts.checker.to_account_info(), 127 | &ctx.accounts.checker_deal_ta.to_account_info(), 128 | &ctx.accounts.token_program.to_account_info() 129 | )?; 130 | } 131 | } 132 | } 133 | 134 | if let Some(Bond { mint, .. }) = ctx.accounts.deal_state.client_bond.as_ref() { 135 | if !cmp_pubkeys(&ctx.accounts.client_bond_mint.key(), mint) { 136 | return Err(InvalidAccount::ClientBondMint.into()) 137 | } 138 | let deal_state_client_bond_ta = Account::::try_from(&ctx.accounts.deal_state_client_bond_ta)?; 139 | check_ta(&deal_state_client_bond_ta, &ctx.accounts.client_bond_mint.key(), &ctx.accounts.deal_state.key())?; 140 | match Account::::try_from(&ctx.accounts.client_bond_ta) { 141 | Ok(client_bond_ta) => { 142 | check_ta(&client_bond_ta, &ctx.accounts.client_bond_mint.key(), ctx.accounts.client.key)?; 143 | }, 144 | Err(_) => { 145 | init_ata( 146 | &ctx.accounts.payer, 147 | &ctx.accounts.client_bond_mint.to_account_info(), 148 | &ctx.accounts.client, 149 | &ctx.accounts.client_bond_ta, 150 | &ctx.accounts.token_program.to_account_info() 151 | )?; 152 | } 153 | } 154 | }; 155 | 156 | if let Some(Bond { mint, .. }) = ctx.accounts.deal_state.executor_bond.as_ref() { 157 | if !cmp_pubkeys(&ctx.accounts.executor_bond_mint.key(), mint) { 158 | return Err(InvalidAccount::ExecutorBondMint.into()) 159 | } 160 | let deal_state_executor_bond_ta = Account::::try_from(&ctx.accounts.deal_state_executor_bond_ta)?; 161 | check_ta(&deal_state_executor_bond_ta, &ctx.accounts.executor_bond_mint.key(), &ctx.accounts.deal_state.key())?; 162 | match Account::::try_from(&ctx.accounts.executor_bond_ta) { 163 | Ok(executor_bond_ta) => { 164 | check_ta(&executor_bond_ta, &ctx.accounts.executor_bond_mint.key(), ctx.accounts.executor.key)?; 165 | }, 166 | Err(_) => { 167 | init_ata( 168 | &ctx.accounts.payer, 169 | &ctx.accounts.executor_bond_mint.to_account_info(), 170 | &ctx.accounts.executor, 171 | &ctx.accounts.executor_bond_ta, 172 | &ctx.accounts.token_program.to_account_info() 173 | )?; 174 | } 175 | } 176 | }; 177 | 178 | Ok(()) 179 | } 180 | 181 | fn transfer_payment(&self) -> Result { 182 | let amount_to_transfer = self.deal_state.amount.saturating_sub( self.deal_state.paid_amount ); 183 | 184 | if amount_to_transfer > 0 { 185 | token::transfer(CpiContext::new_with_signer(self.token_program.to_account_info(), Transfer { 186 | from: self.deal_state_deal_ta.to_account_info(), 187 | to: self.executor_deal_ta.to_account_info(), 188 | authority: self.deal_state.to_account_info(), 189 | }, &[&self.deal_state.seeds()]), amount_to_transfer)?; 190 | } 191 | Ok(PaymentTransfered) 192 | } 193 | 194 | fn transfer_checker_fee(&self) -> Result { 195 | if let Some(Checker { checker_fee, .. }) = self.deal_state.checker { 196 | if checker_fee > 0 { 197 | token::transfer(CpiContext::new_with_signer(self.token_program.to_account_info(), Transfer { 198 | from: self.deal_state_deal_ta.to_account_info(), 199 | to: self.checker_deal_ta.to_account_info(), 200 | authority: self.deal_state.to_account_info(), 201 | }, &[&self.deal_state.seeds()[..]]), checker_fee)?; 202 | } 203 | } 204 | Ok(CheckerFeeTransfered) 205 | } 206 | 207 | fn transfer_bonds(&self) -> Result { 208 | if let Some(Bond{ amount, .. }) = self.deal_state.client_bond { 209 | if amount > 0 { 210 | token::transfer(CpiContext::new_with_signer(self.token_program.to_account_info(), Transfer { 211 | from: self.deal_state_client_bond_ta.to_account_info(), 212 | to: self.client_bond_ta.to_account_info(), 213 | authority: self.deal_state.to_account_info(), 214 | }, &[&self.deal_state.seeds()[..]]), amount)?; 215 | } 216 | } 217 | if let Some(Bond{ amount, .. }) = self.deal_state.executor_bond { 218 | if amount > 0 { 219 | token::transfer(CpiContext::new_with_signer(self.token_program.to_account_info(), Transfer { 220 | from: self.deal_state_executor_bond_ta.to_account_info(), 221 | to: self.executor_bond_ta.to_account_info(), 222 | authority: self.deal_state.to_account_info(), 223 | }, &[&self.deal_state.seeds()[..]]), amount)?; 224 | } 225 | } 226 | 227 | if self.deal_state.client_bond.is_some() { 228 | self.close_deal_state_ta(&self.deal_state_client_bond_ta.clone())?; 229 | } 230 | if self.deal_state.executor_bond.is_some() && !cmp_pubkeys(self.deal_state_client_bond_ta.key, self.deal_state_executor_bond_ta.key) { 231 | self.close_deal_state_ta(&self.deal_state_executor_bond_ta.clone())?; 232 | } 233 | 234 | Ok(BondsTransfered) 235 | } 236 | 237 | fn handle_holder_mode(&self) -> Result { 238 | if let Some(amount) = self.deal_state.holder_mode { 239 | token::transfer( 240 | CpiContext::new_with_signer(self.token_program.to_account_info(), 241 | Transfer { 242 | from: self.deal_state_holder_ta.to_account_info(), 243 | to: self.client_holder_ta.to_account_info(), 244 | authority: self.deal_state.to_account_info() 245 | }, 246 | &[&self.deal_state.seeds()[..]] 247 | ), 248 | amount 249 | )?; 250 | } 251 | 252 | Ok(HolderModeHandled) 253 | } 254 | 255 | fn close_deal_state_ta(&self, token_account: &AccountInfo<'info>) -> Result { 256 | token::close_account( 257 | CpiContext::new_with_signer(self.token_program.to_account_info(), CloseAccount { 258 | account: token_account.clone(), 259 | destination: self.service_fee.to_account_info(), 260 | authority: self.deal_state.to_account_info(), 261 | }, &[&self.deal_state.seeds()[..]]))?; 262 | Ok(AccountClosed) 263 | } 264 | } 265 | 266 | #[allow(dead_code)] 267 | struct Checklist { 268 | checker_fee_transfered: CheckerFeeTransfered, 269 | payment_transfered: PaymentTransfered, 270 | bonds_transfered: BondsTransfered, 271 | holder_mode_handled: HolderModeHandled, 272 | deal_state_deal_ta_closed: AccountClosed, 273 | } 274 | 275 | 276 | #[access_control(Finish::check_accounts(&ctx))] 277 | pub fn handle(ctx: Context) -> Result<()> { 278 | let payment_transfered = ctx.accounts.transfer_payment()?; 279 | let checker_fee_transfered = ctx.accounts.transfer_checker_fee()?; 280 | let bonds_transfered = ctx.accounts.transfer_bonds()?; 281 | let holder_mode_handled = ctx.accounts.handle_holder_mode()?; 282 | 283 | let deal_state_deal_ta_closed = if ctx.accounts.deal_state_deal_ta.to_account_info().lamports() == 0 { 284 | AccountClosed 285 | } else { 286 | ctx.accounts.close_deal_state_ta(&ctx.accounts.deal_state_deal_ta.to_account_info())? 287 | }; 288 | 289 | Checklist { 290 | checker_fee_transfered, 291 | payment_transfered, 292 | bonds_transfered, 293 | holder_mode_handled, 294 | deal_state_deal_ta_closed, 295 | }; 296 | 297 | Ok(()) 298 | } 299 | -------------------------------------------------------------------------------- /programs/deal_contract/src/instructions/initialize.rs: -------------------------------------------------------------------------------- 1 | use anchor_lang::prelude::*; 2 | 3 | use anchor_spl::{token::{ 4 | self, Mint, TokenAccount, Transfer, Token, 5 | }, token_interface::spl_token_2022::cmp_pubkeys, associated_token::AssociatedToken}; 6 | 7 | use crate::{constants::*, 8 | errors::{ErrorCodes, InvalidAccount}, 9 | state::{DealState, Bond, Checker }, 10 | utils::{DeadlineChecked, DealStateCreated, BondsTransfered, HolderModeHandled, DepositTransfered, CheckerFeeTransfered, DealAmountChecked, check_ta, init_ata, AdvancePaymentTransfered}}; 11 | 12 | #[derive(AnchorSerialize, AnchorDeserialize, Clone)] 13 | pub struct InitializeArgs { 14 | pub id: [u8; 16], 15 | pub deal_amount: u64, 16 | pub service_fee: u64, 17 | pub deadline_ts: Option, 18 | pub holder_mode: bool, 19 | pub client_bond: Option, 20 | pub executor_bond: Option, 21 | pub checker_fee: Option, 22 | pub advance_payment_amount: u64, 23 | } 24 | 25 | #[derive(Accounts)] 26 | #[instruction(args: InitializeArgs)] 27 | pub struct Initialize<'info> { 28 | /// CHECK: This is not dangerous because we don't read or write from this account 29 | #[account( 30 | signer, 31 | constraint = !cmp_pubkeys(executor.to_account_info().key, client.to_account_info().key) 32 | )] 33 | pub client: AccountInfo<'info>, 34 | /// CHECK: This is not dangerous because we don't read or write from this account 35 | #[account(signer)] 36 | pub executor: AccountInfo<'info>, 37 | /// CHECK: This is not dangerous because we don't read or write from this account 38 | #[account(mut, signer)] 39 | pub payer: AccountInfo<'info>, 40 | /// CHECK: This is not dangerous because we don't read or write from this account 41 | #[account(mut, signer)] 42 | pub checker: AccountInfo<'info>, 43 | 44 | pub deal_mint: Box>, 45 | /// CHECK: in access_control if client_bond.is_some() 46 | pub client_bond_mint: AccountInfo<'info>, 47 | /// CHECK: in access_control if executor_bond.is_some() 48 | pub executor_bond_mint: AccountInfo<'info>, 49 | pub service_mint: Box>, 50 | #[account(address = HOLDER_MINT )] 51 | pub holder_mint: Box>, 52 | 53 | /// CHECK: by address 54 | #[account(address = SERVICE_FEE_OWNER)] 55 | pub service_fee_owner: AccountInfo<'info>, 56 | #[account(init_if_needed, payer = payer, 57 | associated_token::mint = service_mint, 58 | associated_token::authority = service_fee_owner, 59 | )] 60 | pub service_fee_ta: Box>, 61 | 62 | #[account(init_if_needed, payer = payer, 63 | associated_token::mint = service_mint, 64 | associated_token::authority = client, 65 | )] 66 | pub client_service_ta: Box>, 67 | 68 | #[account(init_if_needed, payer = payer, 69 | associated_token::mint = deal_mint, 70 | associated_token::authority = client, 71 | )] 72 | pub client_deal_ta: Box>, 73 | #[account(init_if_needed, payer = payer, 74 | associated_token::mint = deal_mint, 75 | associated_token::authority = executor, 76 | )] 77 | pub executor_deal_ta: Box>, 78 | #[account(init_if_needed, payer = payer, 79 | associated_token::mint = deal_mint, 80 | associated_token::authority = deal_state, 81 | )] 82 | pub deal_state_deal_ta: Box>, 83 | 84 | /// CHECK: in access_control 85 | #[account(mut)] 86 | pub client_bond_ta: AccountInfo<'info>, 87 | /// CHECK: in access_control 88 | #[account(mut)] 89 | pub executor_bond_ta: AccountInfo<'info>, 90 | /// CHECK: in access_control 91 | #[account(mut)] 92 | pub deal_state_client_bond_ta: AccountInfo<'info>, 93 | /// CHECK: in access_control 94 | #[account(mut)] 95 | pub deal_state_executor_bond_ta: AccountInfo<'info>, 96 | 97 | /// CHECK: must be checked if holder_mode 98 | #[account(mut)] 99 | pub client_holder_ta: AccountInfo<'info>, 100 | /// CHECK: must be initialized if holder_mode 101 | #[account(mut)] 102 | pub deal_state_holder_ta: AccountInfo<'info>, 103 | 104 | #[account(init, 105 | seeds = [&args.id, DEAL_STATE_SEED, client.key.as_ref(), executor.key.as_ref()], 106 | bump, 107 | payer = payer, 108 | space = 8 + std::mem::size_of::() // 8 is for anchor discriminator 109 | )] 110 | pub deal_state: Box>, 111 | pub system_program: Program<'info, System>, 112 | pub token_program: Program<'info, Token>, 113 | pub associated_token_program: Program<'info, AssociatedToken>, 114 | } 115 | 116 | 117 | #[allow(dead_code)] 118 | struct Checklist { 119 | pub deadline_checked: DeadlineChecked, 120 | pub amount_checked: DealAmountChecked, 121 | 122 | pub deal_state_created: DealStateCreated, 123 | pub bonds_transfered: BondsTransfered, 124 | pub holder_mode_handled: HolderModeHandled, 125 | pub deposit_transfered: DepositTransfered, 126 | pub checker_fee_transfered: CheckerFeeTransfered, 127 | 128 | pub advance_payment_transfered: AdvancePaymentTransfered, 129 | } 130 | 131 | impl<'info> Initialize<'info> { 132 | fn check_accounts(ctx: &Context, args: &InitializeArgs) -> Result<()> { 133 | if args.client_bond.is_some() { 134 | Account::::try_from(&ctx.accounts.client_bond_mint).map_err(|_|InvalidAccount::ClientBondMint)?; 135 | 136 | let client_bond_ta = Account::::try_from(&ctx.accounts.client_bond_ta)?; 137 | check_ta(&client_bond_ta, &ctx.accounts.client_bond_mint.key(), ctx.accounts.client.key) 138 | .map_err(|_|InvalidAccount::ClientBondTokenAccount)?; 139 | 140 | match Account::::try_from(&ctx.accounts.deal_state_client_bond_ta) { 141 | Ok(deal_state_client_bond_ta) => { 142 | check_ta(&deal_state_client_bond_ta, &ctx.accounts.client_bond_mint.key(), &ctx.accounts.deal_state.key()) 143 | .map_err(|_|InvalidAccount::DealStateClientBondTokenAccount)?; 144 | }, 145 | Err(_) => { 146 | init_ata( 147 | &ctx.accounts.payer, 148 | &ctx.accounts.client_bond_mint.to_account_info(), 149 | &ctx.accounts.deal_state.to_account_info(), 150 | &ctx.accounts.deal_state_client_bond_ta, 151 | &ctx.accounts.token_program.to_account_info() 152 | )?; 153 | } 154 | }; 155 | }; 156 | 157 | if args.executor_bond.is_some() { 158 | Account::::try_from(&ctx.accounts.executor_bond_mint).map_err(|_|InvalidAccount::ExecutorBondMint)?; 159 | 160 | let executor_bond_ta = Account::::try_from(&ctx.accounts.executor_bond_ta)?; 161 | check_ta(&executor_bond_ta, &ctx.accounts.executor_bond_mint.key(), ctx.accounts.executor.key) 162 | .map_err(|_|InvalidAccount::ExecutorBondTokenAccount)?; 163 | 164 | match Account::::try_from(&ctx.accounts.deal_state_executor_bond_ta) { 165 | Ok(deal_state_executor_bond_ta) => { 166 | check_ta(&deal_state_executor_bond_ta, &ctx.accounts.executor_bond_mint.key(), &ctx.accounts.deal_state.key()) 167 | .map_err(|_|InvalidAccount::DealStateExecutorBondTokenAccount)?; 168 | }, 169 | Err(_) => { 170 | init_ata( 171 | &ctx.accounts.payer, 172 | &ctx.accounts.executor_bond_mint.to_account_info(), 173 | &ctx.accounts.deal_state.to_account_info(), 174 | &ctx.accounts.deal_state_executor_bond_ta, 175 | &ctx.accounts.token_program.to_account_info() 176 | )?; 177 | } 178 | }; 179 | }; 180 | 181 | if args.holder_mode { 182 | let client_holder_ta = Account::::try_from(&ctx.accounts.client_holder_ta)?; 183 | check_ta(&client_holder_ta, &HOLDER_MINT, ctx.accounts.client.key).map_err(|_|InvalidAccount::ClientHolderTokenAccount)?; 184 | 185 | if ctx.accounts.client_deal_ta.mint != SERVICE_FEE_MINT { 186 | return Err(ErrorCodes::HolderModeUnavailable.into()); 187 | } 188 | 189 | match Account::::try_from(&ctx.accounts.deal_state_holder_ta) { 190 | Ok(deal_state_holder_ta ) => { 191 | check_ta(&deal_state_holder_ta, &HOLDER_MINT, ctx.accounts.deal_state.to_account_info().key) 192 | .map_err(|_|InvalidAccount::DealStateHolderTokenAccount)?; 193 | }, 194 | Err(_) => { 195 | init_ata( 196 | &ctx.accounts.payer, 197 | &ctx.accounts.holder_mint.to_account_info(), 198 | &ctx.accounts.deal_state.to_account_info(), 199 | &ctx.accounts.deal_state_holder_ta, 200 | &ctx.accounts.token_program.to_account_info() 201 | )?; 202 | } 203 | }; 204 | } 205 | 206 | Ok(()) 207 | } 208 | 209 | fn check_deadline(&self) -> Result { 210 | if self.deal_state.deadline_expired() { 211 | return Err(ErrorCodes::DeadlineExpired.into()) 212 | }; 213 | Ok(DeadlineChecked) 214 | } 215 | 216 | fn check_deal_amount(&self, deal_amount: u64) -> Result { 217 | if deal_amount == 0 { 218 | return Err(ErrorCodes::AmountTooLow.into()); 219 | } 220 | Ok(DealAmountChecked) 221 | } 222 | 223 | fn transfer_bonds(&self, client_bond: Option, executor_bond: Option) -> Result { 224 | if let Some(amount) = client_bond.as_ref() { 225 | if *amount > 0 { 226 | anchor_spl::token::transfer(CpiContext::new( 227 | self.token_program.to_account_info(), 228 | token::Transfer { 229 | from: self.client_bond_ta.to_account_info(), 230 | to: self.deal_state_client_bond_ta.to_account_info(), 231 | authority: self.client.to_account_info(), 232 | }, 233 | ), *amount)?; 234 | } 235 | } 236 | if let Some(amount) = executor_bond.as_ref() { 237 | if *amount > 0 { 238 | anchor_spl::token::transfer(CpiContext::new( 239 | self.token_program.to_account_info(), 240 | token::Transfer { 241 | from: self.executor_bond_ta.to_account_info(), 242 | to: self.deal_state_client_bond_ta.to_account_info(), 243 | authority: self.executor.to_account_info(), 244 | }, 245 | ), *amount)?; 246 | } 247 | } 248 | Ok(BondsTransfered) 249 | } 250 | 251 | fn transfer_deposit(&self, amount: u64) -> Result { 252 | anchor_spl::token::transfer(CpiContext::new( 253 | self.token_program.to_account_info(), 254 | token::Transfer { 255 | from: self.client_deal_ta.to_account_info(), 256 | to: self.deal_state_deal_ta.to_account_info(), 257 | authority: self.client.to_account_info(), 258 | }, 259 | ), amount)?; 260 | Ok(DepositTransfered) 261 | } 262 | 263 | fn handle_service_fee(&self, holder_mode: bool, service_fee: u64) -> Result { 264 | if !holder_mode && service_fee == 0 { 265 | return Err(ErrorCodes::FeeIsTooLow.into()); 266 | }; 267 | 268 | if holder_mode { 269 | let cpi_accounts = Transfer { 270 | from: self.client_holder_ta.to_account_info(), 271 | to: self.deal_state_holder_ta.to_account_info(), 272 | authority: self.client.clone(), 273 | }; 274 | token::transfer(CpiContext::new(self.token_program.to_account_info(), cpi_accounts), HOLDER_MODE_AMOUNT)?; 275 | } else if service_fee > 0 { 276 | let cpi_accounts = Transfer { 277 | from: self.client_deal_ta.to_account_info(), 278 | to: self.service_fee_ta.to_account_info(), 279 | authority: self.client.clone(), 280 | }; 281 | token::transfer(CpiContext::new(self.token_program.to_account_info(), cpi_accounts), service_fee)?; 282 | } 283 | 284 | Ok(HolderModeHandled) 285 | } 286 | 287 | fn transfer_advance_payment(&self, amount: u64) -> Result { 288 | let cpi_accounts = Transfer { 289 | from: self.deal_state_deal_ta.to_account_info(), 290 | to: self.executor_deal_ta.to_account_info(), 291 | authority: self.deal_state.to_account_info(), 292 | }; 293 | token::transfer( 294 | CpiContext::new(self.token_program.to_account_info(), cpi_accounts) 295 | .with_signer(&[&self.deal_state.seeds()]), 296 | amount)?; 297 | 298 | Ok(AdvancePaymentTransfered) 299 | } 300 | } 301 | 302 | #[access_control(Initialize::check_accounts(&ctx, &args))] 303 | pub fn handle(ctx: Context, args: InitializeArgs) -> Result<()> { 304 | let deal_state_created = { 305 | **ctx.accounts.deal_state = DealState { 306 | id: args.id, 307 | client_key: *ctx.accounts.client.key, 308 | executor_key: *ctx.accounts.executor.to_account_info().key, 309 | 310 | bump: [*ctx.bumps.get("deal_state").unwrap()], 311 | client_bond: if let Some(amount) = args.client_bond { Some(Bond {mint: ctx.accounts.client_bond_mint.key(), amount}) } else { None }, 312 | executor_bond: if let Some(amount) = args.executor_bond { Some(Bond {mint: ctx.accounts.executor_bond_mint.key(), amount}) } else { None }, 313 | checker: if let Some(checker_fee) = args.checker_fee.as_ref() { 314 | Some(Checker {checker_fee: *checker_fee, checker_key: ctx.accounts.checker.key()}) 315 | } else { None }, 316 | 317 | amount: args.deal_amount, 318 | paid_amount: args.advance_payment_amount, 319 | 320 | deadline_ts: args.deadline_ts, 321 | deal_token_mint: ctx.accounts.deal_mint.to_account_info().key(), 322 | holder_mode: if args.holder_mode { Some(HOLDER_MODE_AMOUNT) } else { None }, 323 | }; 324 | DealStateCreated 325 | }; 326 | 327 | let deadline_checked = ctx.accounts.check_deadline()?; 328 | 329 | let amount_checked = ctx.accounts.check_deal_amount(args.deal_amount)?; 330 | 331 | let holder_mode_handled = ctx.accounts.handle_service_fee(args.holder_mode, args.service_fee)?; 332 | 333 | let (deposit_transfered, checker_fee_transfered) = { 334 | ctx.accounts.transfer_deposit(args.deal_amount + if let Some(checker_fee) = args.checker_fee { checker_fee } else { 0 })?; 335 | (DepositTransfered, CheckerFeeTransfered) 336 | }; 337 | 338 | let bonds_transfered = ctx.accounts.transfer_bonds(args.client_bond, args.executor_bond)?; 339 | 340 | let advance_payment_transfered = ctx.accounts.transfer_advance_payment(args.advance_payment_amount)?; 341 | 342 | Checklist { 343 | deadline_checked, 344 | amount_checked, 345 | checker_fee_transfered, 346 | deposit_transfered, 347 | deal_state_created, 348 | bonds_transfered, 349 | holder_mode_handled, 350 | advance_payment_transfered 351 | }; 352 | 353 | Ok(()) 354 | } 355 | -------------------------------------------------------------------------------- /programs/deal_contract/src/instructions/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cancel; 2 | pub use cancel::*; 3 | 4 | pub mod finish; 5 | pub use finish::*; 6 | 7 | pub mod update_checker; 8 | pub use update_checker::*; 9 | 10 | pub mod initialize; 11 | pub use initialize::*; 12 | 13 | pub mod partially_pay; 14 | pub use partially_pay::*; 15 | -------------------------------------------------------------------------------- /programs/deal_contract/src/instructions/partially_pay.rs: -------------------------------------------------------------------------------- 1 | use anchor_lang::prelude::*; 2 | 3 | use anchor_spl::{token::{ 4 | self, Mint, TokenAccount, Transfer, Token, 5 | }, token_interface::spl_token_2022::cmp_pubkeys, associated_token::AssociatedToken}; 6 | 7 | use crate::{ 8 | state::DealState, 9 | utils::{PaymentTransfered, DealStateUpdated}}; 10 | 11 | #[derive(AnchorSerialize, AnchorDeserialize, Clone)] 12 | pub struct PartiallyPayArgs { 13 | pub amount: u64, 14 | } 15 | 16 | #[derive(Accounts)] 17 | #[instruction(args: PartiallyPayArgs)] 18 | pub struct PartiallyPay<'info> { 19 | /// CHECK: This is not dangerous because we don't read or write from this account 20 | #[account( 21 | signer, 22 | constraint=cmp_pubkeys(&deal_state.client_key, client.key), 23 | constraint = !cmp_pubkeys(executor.to_account_info().key, client.to_account_info().key) 24 | )] 25 | pub client: AccountInfo<'info>, 26 | /// CHECK: This is not dangerous because we don't read or write from this account 27 | #[account(constraint=cmp_pubkeys(&deal_state.executor_key, executor.key))] 28 | pub executor: AccountInfo<'info>, 29 | /// CHECK: This is not dangerous because we don't read or write from this account 30 | #[account(mut, signer)] 31 | pub payer: AccountInfo<'info>, 32 | 33 | #[account(address=deal_state.deal_token_mint)] 34 | pub deal_mint: Box>, 35 | 36 | #[account(mut, 37 | associated_token::mint = deal_mint, 38 | associated_token::authority = client, 39 | )] 40 | pub client_deal_ta: Box>, 41 | #[account(init_if_needed, payer = payer, 42 | associated_token::mint = deal_mint, 43 | associated_token::authority = executor, 44 | )] 45 | pub executor_deal_ta: Box>, 46 | 47 | #[account(mut)] 48 | pub deal_state: Box>, 49 | pub system_program: Program<'info, System>, 50 | pub token_program: Program<'info, Token>, 51 | pub associated_token_program: Program<'info, AssociatedToken>, 52 | } 53 | 54 | 55 | #[allow(dead_code)] 56 | struct Checklist { 57 | pub deal_state_updated: DealStateUpdated, 58 | pub payment_transfered: PaymentTransfered 59 | } 60 | 61 | impl<'info> PartiallyPay<'info> { 62 | fn update_deal_state(&mut self, amount: u64) -> DealStateUpdated { 63 | self.deal_state.paid_amount += amount; 64 | 65 | DealStateUpdated 66 | } 67 | 68 | fn transfer_payment(&self, amount: u64) -> Result { 69 | let cpi_accounts = Transfer { 70 | from: self.client_deal_ta.to_account_info(), 71 | to: self.executor_deal_ta.to_account_info(), 72 | authority: self.client.to_account_info(), 73 | }; 74 | token::transfer(CpiContext::new(self.token_program.to_account_info(), cpi_accounts), amount)?; 75 | 76 | Ok(PaymentTransfered) 77 | } 78 | } 79 | 80 | pub fn handle(ctx: Context, args: PartiallyPayArgs) -> Result<()> { 81 | let deal_state_updated = ctx.accounts.update_deal_state(args.amount); 82 | let payment_transfered = ctx.accounts.transfer_payment(args.amount)?; 83 | 84 | Checklist { 85 | payment_transfered, 86 | deal_state_updated 87 | }; 88 | 89 | Ok(()) 90 | } 91 | -------------------------------------------------------------------------------- /programs/deal_contract/src/instructions/update_checker.rs: -------------------------------------------------------------------------------- 1 | use anchor_lang::prelude::*; 2 | use anchor_spl::{token::Token, token_interface::spl_token_2022::cmp_pubkeys}; 3 | 4 | use crate::{ 5 | constants::*, 6 | errors::ErrorCodes, 7 | state::{Checker, DealState}, 8 | }; 9 | 10 | #[derive(Accounts)] 11 | pub struct UpdateChecker<'info> { 12 | /// CHECK: 13 | #[account(mut, signer)] 14 | pub initializer: AccountInfo<'info>, 15 | /// CHECK: 16 | #[account(mut, address = deal_state.client_key)] 17 | pub client: AccountInfo<'info>, 18 | /// CHECK: 19 | #[account(mut, address = deal_state.executor_key)] 20 | pub executor: AccountInfo<'info>, 21 | /// CHECK: 22 | #[account(signer)] 23 | pub current_checker: AccountInfo<'info>, 24 | /// CHECK: 25 | #[account(signer)] 26 | pub new_checker: AccountInfo<'info>, 27 | 28 | #[account(mut)] 29 | pub deal_state: Box>, 30 | pub token_program: Program<'info, Token>, 31 | } 32 | 33 | impl<'info> UpdateChecker<'info> { 34 | fn check_accounts(ctx: &Context) -> Result<()> { 35 | if ctx.accounts.deal_state.checker.is_some() {} 36 | Ok(()) 37 | } 38 | } 39 | 40 | #[access_control(UpdateChecker::check_accounts(&ctx))] 41 | pub fn handle(ctx: Context, new_checker_fee: u64) -> Result<()> { 42 | if !cmp_pubkeys(ctx.accounts.initializer.key, &SERVICE_ACCOUNT_ADDRESS) { 43 | require!(ctx.accounts.client.is_signer, ErrorCode::AccountNotSigner); 44 | require!(ctx.accounts.executor.is_signer, ErrorCode::AccountNotSigner); 45 | 46 | if let Some(Checker { .. }) = ctx.accounts.deal_state.checker { 47 | return Err(ErrorCodes::DealStateWithChecker.into()); 48 | 49 | // require_keys_eq!( 50 | // checker_key, 51 | // ctx.accounts.current_checker.key(), 52 | // InvalidAccount::Checker 53 | // ); 54 | } 55 | }; 56 | 57 | ctx.accounts.deal_state.checker = Some(Checker { 58 | checker_fee: new_checker_fee, 59 | checker_key: ctx.accounts.new_checker.key(), 60 | }); 61 | 62 | Ok(()) 63 | } 64 | -------------------------------------------------------------------------------- /programs/deal_contract/src/lib.rs: -------------------------------------------------------------------------------- 1 | use anchor_lang::prelude::*; 2 | use std::convert::Into; 3 | 4 | mod constants; 5 | mod errors; 6 | mod instructions; 7 | mod state; 8 | mod utils; 9 | 10 | use instructions::*; 11 | 12 | declare_id!("GKNkN4uDJWmidEC9h5Q9GQXNg48Go6q5bdnkDj6bSopz"); 13 | 14 | #[program] 15 | pub mod deal_contract { 16 | 17 | use super::*; 18 | 19 | pub fn initialize(ctx: Context, args: InitializeArgs) -> Result<()> { 20 | instructions::initialize::handle(ctx, args) 21 | } 22 | 23 | pub fn finish(ctx: Context) -> Result<()> { 24 | instructions::finish::handle(ctx) 25 | } 26 | 27 | pub fn cancel(ctx: Context) -> Result<()> { 28 | instructions::cancel::handle(ctx) 29 | } 30 | 31 | pub fn update_checker(ctx: Context, new_checker_fee: u64) -> Result<()> { 32 | instructions::update_checker::handle(ctx, new_checker_fee) 33 | } 34 | 35 | pub fn partially_pay(ctx: Context, args: PartiallyPayArgs) -> Result<()> { 36 | instructions::partially_pay::handle(ctx, args) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /programs/deal_contract/src/state/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{constants::DEAL_STATE_SEED, errors::ErrorCodes}; 2 | pub use anchor_lang::prelude::*; 3 | 4 | #[derive(AnchorSerialize, AnchorDeserialize, Clone)] 5 | pub struct Bond { 6 | pub mint: Pubkey, 7 | pub amount: u64, 8 | } 9 | #[derive(AnchorSerialize, AnchorDeserialize, Clone)] 10 | pub struct Checker { 11 | pub checker_fee: u64, 12 | pub checker_key: Pubkey, 13 | } 14 | 15 | #[account] 16 | pub struct DealState { 17 | pub id: [u8; 16], 18 | pub client_key: Pubkey, 19 | pub executor_key: Pubkey, 20 | 21 | pub deal_token_mint: Pubkey, 22 | 23 | pub client_bond: Option, 24 | pub executor_bond: Option, 25 | 26 | pub checker: Option, 27 | 28 | pub holder_mode: Option, 29 | 30 | pub amount: u64, 31 | pub paid_amount: u64, 32 | 33 | pub deadline_ts: Option, 34 | 35 | pub bump: [u8; 1], 36 | } 37 | 38 | impl DealState { 39 | pub fn id(&self) -> u128 { 40 | u128::from_le_bytes(self.id) 41 | } 42 | 43 | pub fn bump(&self) -> u8 { 44 | self.bump[0] 45 | } 46 | 47 | pub fn seeds(&self) -> [&[u8]; 5] { 48 | [ 49 | &self.id[..], 50 | DEAL_STATE_SEED, 51 | self.client_key.as_ref(), 52 | self.executor_key.as_ref(), 53 | &self.bump, 54 | ] 55 | } 56 | 57 | pub fn client_bond(&self) -> Result<&Bond> { 58 | Ok(self.client_bond.as_ref().ok_or(ErrorCodes::NoClientBond)?) 59 | } 60 | pub fn client_bond_mut(&mut self) -> Result<&mut Bond> { 61 | Ok(self.client_bond.as_mut().ok_or(ErrorCodes::NoClientBond)?) 62 | } 63 | pub fn executor_bond(&self) -> Result<&Bond> { 64 | Ok(self.executor_bond.as_ref().ok_or(ErrorCodes::NoExecutorBond)?) 65 | } 66 | pub fn executor_bond_mut(&mut self) -> Result<&mut Bond> { 67 | Ok(self.executor_bond.as_mut().ok_or(ErrorCodes::NoExecutorBond)?) 68 | } 69 | pub fn with_checker(&self) -> Result<&Checker> { 70 | Ok(self.checker.as_ref().ok_or(ErrorCodes::DealStateNotWithChecker)?) 71 | } 72 | pub fn with_checker_mut(&mut self) -> Result<&mut Checker> { 73 | Ok(self.checker.as_mut().ok_or(ErrorCodes::DealStateNotWithChecker)?) 74 | } 75 | 76 | pub fn deadline_expired(&self) -> bool { 77 | match self.deadline_ts { 78 | Some(deadline_ts) => { 79 | let current_ts = Clock::get().expect("Failed to get Clock SysVar").unix_timestamp; 80 | if deadline_ts < current_ts { 81 | true 82 | } else { 83 | false 84 | } 85 | } 86 | None => false, 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /programs/deal_contract/src/utils/checklist.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /programs/deal_contract/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | use anchor_lang::prelude::*; 2 | use anchor_spl::{token::TokenAccount, token_interface::spl_token_2022::cmp_pubkeys}; 3 | 4 | use crate::errors::ErrorCodes; 5 | 6 | pub(crate) mod checklist; 7 | 8 | // pub(crate) struct SignaturesChecked; 9 | pub(crate) struct DealStateCreated; 10 | pub(crate) struct DealStateUpdated; 11 | pub(crate) struct DeadlineChecked; 12 | pub(crate) struct DealAmountChecked; 13 | 14 | pub(crate) struct CheckerFeeTransfered; 15 | pub(crate) struct DepositTransfered; 16 | pub(crate) struct BondsTransfered; 17 | pub(crate) struct HolderModeHandled; 18 | 19 | pub(crate) struct PaymentTransfered; 20 | pub(crate) struct AdvancePaymentTransfered; 21 | // pub(crate) struct PaymentReturned; 22 | 23 | // pub(crate) struct CheckerAccountsChecked; 24 | // pub(crate) struct BondAccountsChecked; 25 | 26 | pub(crate) struct AccountClosed; 27 | 28 | pub fn init_ata<'a, 'info>( 29 | payer: &'a AccountInfo<'info>, 30 | mint: &'a AccountInfo<'info>, 31 | authority: &'a AccountInfo<'info>, 32 | ata: &'a AccountInfo<'info>, 33 | token_program: &'a AccountInfo<'info>, 34 | ) -> Result<()> { 35 | solana_program::program::invoke( 36 | &spl_associated_token_account::instruction::create_associated_token_account( 37 | payer.key, 38 | authority.key, 39 | mint.key, 40 | token_program.key, 41 | ), 42 | &[ 43 | payer.clone(), 44 | mint.clone(), 45 | authority.clone(), 46 | ata.clone(), 47 | token_program.clone(), 48 | ], 49 | )?; 50 | 51 | Ok(()) 52 | } 53 | 54 | pub fn check_ta<'info>( 55 | token_account: &Account<'info, TokenAccount>, 56 | expected_mint: &Pubkey, 57 | expected_owner: &Pubkey, 58 | ) -> Result<()> { 59 | if !cmp_pubkeys(&token_account.mint, &expected_mint) { 60 | return Err(ErrorCodes::InvalidMint.into()); 61 | }; 62 | if !cmp_pubkeys(&token_account.owner, &expected_owner) { 63 | return Err(ErrorCodes::InvalidOwner.into()); 64 | }; 65 | Ok(()) 66 | } 67 | -------------------------------------------------------------------------------- /tests/client.ts: -------------------------------------------------------------------------------- 1 | import * as anchor from "@coral-xyz/anchor"; 2 | import { Program, BN, IdlTypes } from "@coral-xyz/anchor"; 3 | import { PublicKey, Keypair, TransactionInstruction, VersionedTransaction, Signer, AddressLookupTableAccount, TransactionMessage, Connection, ComputeBudgetProgram } from '@solana/web3.js'; 4 | import { TOKEN_PROGRAM_ID, createAccount, getAssociatedTokenAddressSync } from "@solana/spl-token"; 5 | import { DealContract } from "../target/types/deal_contract"; 6 | import { ASSOCIATED_PROGRAM_ID } from "@coral-xyz/anchor/dist/cjs/utils/token"; 7 | 8 | export const DEAL_STATE_SEED: string = "deal_state"; 9 | 10 | export const ENCODER = anchor.utils.bytes.utf8; 11 | 12 | export const SERVICE_FEE_OWNER_KP: Keypair = Keypair.generate(); // FIXME 13 | export const SERVICE_FEE_MINT_KP: Keypair = Keypair.generate(); 14 | 15 | export const SERVICE_FEE_OWNER: PublicKey = new PublicKey("C8pHACh7SAZWVZvgppM6VkWEDC6voLGGctch5Vr5hkEz"); // FIXME 16 | export const SERVICE_FEE_MINT: PublicKey = new PublicKey("62PtWFh2dQ69LKbHumBpMa7wG71r7i7Damwo2wMYfcR1"); 17 | export const SERVICE_FEE_TA: PublicKey = getAssociatedTokenAddressSync(SERVICE_FEE_MINT, SERVICE_FEE_OWNER); 18 | 19 | export const HOLDER_MINT: PublicKey = new PublicKey("64esx9p99rgwzmBCFCaUDCKJL2b2WrgdFe7chyaDyrKD"); // FIXME 20 | 21 | export const DEAL_CONTRACT_PROGRAM_ID: PublicKey = new PublicKey("GKNkN4uDJWmidEC9h5Q9GQXNg48Go6q5bdnkDj6bSopz"); 22 | 23 | 24 | export function uuidTodealIdBuf (uuid: string): Buffer { 25 | const uuidhex = uuid.replace(/-/g, ''); 26 | if (uuid.length != 36 || uuidhex.length != 32) throw new Error(`Invalid uuid UUID: ${uuid}`); 27 | return Buffer.from(uuidhex, 'hex') 28 | } 29 | 30 | export function getDealStatePk(dealId: Buffer, clientPk: PublicKey, executorPk: PublicKey, programId?: PublicKey): [PublicKey, number] { 31 | return PublicKey.findProgramAddressSync([ 32 | dealId, 33 | ENCODER.encode(DEAL_STATE_SEED), 34 | clientPk.toBuffer(), 35 | executorPk.toBuffer(), 36 | ], programId ? programId : DEAL_CONTRACT_PROGRAM_ID) 37 | } 38 | 39 | export async function signAndSendIxs( 40 | connection: Connection, 41 | instructions: TransactionInstruction[], 42 | signers: Signer[] = [], 43 | payer?: Signer, 44 | lookupTable?: AddressLookupTableAccount[]) 45 | : Promise { 46 | if (!payer && signers.length == 0) { throw new Error("no payer has been assigned")}; 47 | const blockhash = (await connection.getLatestBlockhash()); 48 | const msg = new TransactionMessage({ 49 | payerKey: payer ? payer.publicKey : signers[0].publicKey, 50 | recentBlockhash: blockhash.blockhash, 51 | instructions 52 | }).compileToV0Message(lookupTable); 53 | 54 | const tx = new VersionedTransaction(msg); 55 | tx.sign(signers) 56 | 57 | const simulation = await connection.simulateTransaction(tx); 58 | if (simulation.value.err) {throw simulation.value} 59 | 60 | const rawTx = tx.serialize(); 61 | if (rawTx.length > 1200) { 62 | console.error(`TX_LENGTH: ${rawTx.length}`) 63 | } 64 | const signature = await connection.sendRawTransaction(rawTx) 65 | const txStatus = (await connection.confirmTransaction({signature,...blockhash}, "confirmed")).value; 66 | 67 | if (!!txStatus.err) {throw signature} 68 | return signature 69 | } 70 | 71 | export const getTotalComputeIxs = ( 72 | compute: number, 73 | priorityMicroLamports = 1 74 | ) => { 75 | const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({ 76 | units: compute, 77 | }); 78 | const addPriorityFee = ComputeBudgetProgram.setComputeUnitPrice({ 79 | microLamports: priorityMicroLamports, 80 | }); 81 | return [modifyComputeUnits, addPriorityFee]; 82 | }; 83 | 84 | export async function getInitializeIx ({ 85 | dealContractProgram, 86 | dealId, 87 | amount, 88 | serviceFee, 89 | clientPk, 90 | executorPk, 91 | payerPk, 92 | dealMint, 93 | holderMode, 94 | deadline, 95 | withChecker = null, 96 | clientBond = null, 97 | executorBond = null, 98 | }: { 99 | dealContractProgram: Program, 100 | dealId: string | Buffer, 101 | amount: number, 102 | serviceFee: { 103 | amount: number, 104 | mint?: PublicKey 105 | }, 106 | clientPk: PublicKey, 107 | executorPk: PublicKey, 108 | payerPk: PublicKey, 109 | dealMint: PublicKey, 110 | deadline?: number, 111 | holderMode?: boolean, 112 | withChecker?: { 113 | checkerFee: BN, 114 | checkerKey: PublicKey 115 | }, 116 | clientBond?: IdlTypes["Bond"], 117 | executorBond?: IdlTypes["Bond"], 118 | }) { 119 | if (!(dealId instanceof Buffer)) {dealId = uuidTodealIdBuf(dealId)} 120 | dealId = dealId as Buffer; 121 | const dealState = getDealStatePk(dealId, clientPk, executorPk)[0]; 122 | 123 | const dealStateDealTa = getAssociatedTokenAddressSync(dealMint, dealState, true); 124 | const clientDealTa = getAssociatedTokenAddressSync(dealMint, clientPk); 125 | const executorDealTa = getAssociatedTokenAddressSync(dealMint, executorPk); 126 | 127 | const serviceFeeTa = serviceFee.mint ? getAssociatedTokenAddressSync(serviceFee.mint, SERVICE_FEE_OWNER) : SERVICE_FEE_TA; 128 | 129 | return dealContractProgram.methods.initialize({ 130 | id: Array.from(dealId), 131 | dealAmount: new anchor.BN(amount), 132 | serviceFee: new anchor.BN(serviceFee.amount), 133 | deadlineTs: deadline !== undefined ? new BN(deadline) : null, 134 | holderMode: !!holderMode, 135 | checkerFee: !!withChecker ? withChecker.checkerFee : null, 136 | clientBond: clientBond ? clientBond.amount : null, 137 | executorBond: executorBond ? executorBond.amount : null, 138 | }) 139 | .accountsStrict({ 140 | client: clientPk, 141 | executor: executorPk, 142 | checker: withChecker ? withChecker.checkerKey : payerPk, 143 | payer: payerPk, 144 | 145 | dealMint, 146 | clientBondMint: clientBond ? clientBond.mint : dealMint, 147 | executorBondMint: executorBond ? executorBond.mint : dealMint, 148 | serviceMint: !!serviceFee.mint ? serviceFee.mint : SERVICE_FEE_MINT, 149 | 150 | clientHolderTa: holderMode ? getAssociatedTokenAddressSync(HOLDER_MINT, clientPk) : clientDealTa, 151 | clientBondTa: clientBond ? getAssociatedTokenAddressSync(clientBond.mint, clientPk) : clientDealTa, 152 | clientDealTa, 153 | clientServiceTa: getAssociatedTokenAddressSync(!!serviceFee.mint ? serviceFee.mint : SERVICE_FEE_MINT, clientPk), 154 | 155 | dealStateClientBondTa: clientBond ? getAssociatedTokenAddressSync(clientBond.mint, dealState, true) : dealStateDealTa, 156 | dealStateDealTa, 157 | dealStateExecutorBondTa: executorBond ? getAssociatedTokenAddressSync(executorBond.mint, dealState, true) : dealStateDealTa, 158 | dealStateHolderTa: holderMode ? getAssociatedTokenAddressSync(HOLDER_MINT, dealState, true) : dealStateDealTa, 159 | 160 | executorBondTa: executorBond ? getAssociatedTokenAddressSync(executorBond.mint, executorPk) : executorDealTa, 161 | executorDealTa, 162 | 163 | serviceFeeOwner: SERVICE_FEE_OWNER, 164 | serviceFeeTa, 165 | 166 | dealState, 167 | holderMint: HOLDER_MINT, 168 | associatedTokenProgram: ASSOCIATED_PROGRAM_ID, 169 | tokenProgram: TOKEN_PROGRAM_ID, 170 | systemProgram: anchor.web3.SystemProgram.programId, 171 | }).preInstructions([getTotalComputeIxs(400000)[0]]); 172 | } 173 | 174 | 175 | export async function getCancelIx ({ 176 | dealContractProgram, initializer, dealId, clientPk, executorPk, payerPk, dealMint, checkerKey = null, clientBondMint, executorBondMint 177 | }: { 178 | dealContractProgram: Program, 179 | 180 | initializer: PublicKey, 181 | dealId: string | Buffer, 182 | clientPk: PublicKey, 183 | executorPk: PublicKey, 184 | payerPk: PublicKey, 185 | dealMint: PublicKey, 186 | 187 | checkerKey?: PublicKey, 188 | clientBondMint?: PublicKey, 189 | executorBondMint?: PublicKey, 190 | }) { 191 | if (!(dealId instanceof Buffer)) {dealId = uuidTodealIdBuf(dealId)} 192 | dealId = dealId as Buffer; 193 | const dealState = getDealStatePk(dealId, clientPk, executorPk)[0]; 194 | 195 | const dealStateDealTa = getAssociatedTokenAddressSync(dealMint, dealState, true); 196 | const clientDealTa = getAssociatedTokenAddressSync(dealMint, clientPk); 197 | const executorDealTa = getAssociatedTokenAddressSync(dealMint, executorPk); 198 | 199 | const checkerDealTa = getAssociatedTokenAddressSync(dealMint, checkerKey ? checkerKey: initializer); 200 | 201 | return dealContractProgram.methods.cancel() 202 | .accountsStrict({ 203 | initializer, 204 | client: clientPk, 205 | executor: executorPk, 206 | payer: payerPk, 207 | checkerDealTa, 208 | checker: checkerKey ? checkerKey : initializer, 209 | 210 | dealMint, 211 | clientBondMint: clientBondMint ? clientBondMint : dealMint, 212 | executorBondMint: executorBondMint ? executorBondMint : dealMint, 213 | 214 | clientBondTa: clientBondMint ? getAssociatedTokenAddressSync(clientBondMint, clientPk) : clientDealTa, 215 | clientDealTa, 216 | 217 | dealStateClientBondTa: clientBondMint ? getAssociatedTokenAddressSync(clientBondMint, dealState, true) : dealStateDealTa, 218 | dealStateDealTa, 219 | dealStateExecutorBondTa: executorBondMint ? getAssociatedTokenAddressSync(executorBondMint, dealState, true) : dealStateDealTa, 220 | 221 | executorBondTa: executorBondMint ? getAssociatedTokenAddressSync(executorBondMint, executorPk) : executorDealTa, 222 | 223 | dealState, 224 | associatedTokenProgram: ASSOCIATED_PROGRAM_ID, 225 | tokenProgram: TOKEN_PROGRAM_ID, 226 | systemProgram: anchor.web3.SystemProgram.programId, 227 | }).preInstructions([getTotalComputeIxs(400000)[0]]) 228 | } 229 | 230 | export async function getFinishIx ({ 231 | dealContractProgram, 232 | initializer, 233 | dealId, 234 | clientPk, 235 | executorPk, 236 | dealMint, 237 | holderMode, 238 | payerPk, 239 | checkerKey = null, 240 | clientBond = null, 241 | executorBond = null, 242 | }: { 243 | dealContractProgram: Program, 244 | dealId: string | Buffer, 245 | initializer: PublicKey, 246 | clientPk: PublicKey, 247 | executorPk: PublicKey, 248 | dealMint: PublicKey, 249 | payerPk: PublicKey, 250 | holderMode?: boolean, 251 | checkerKey?: PublicKey, 252 | clientBond?: IdlTypes["Bond"], 253 | executorBond?: IdlTypes["Bond"], 254 | }) { 255 | if (!(dealId instanceof Buffer)) {dealId = uuidTodealIdBuf(dealId)} 256 | dealId = dealId as Buffer; 257 | const dealState = getDealStatePk(dealId, clientPk, executorPk)[0]; 258 | 259 | const dealStateDealTa = getAssociatedTokenAddressSync(dealMint, dealState, true); 260 | const clientDealTa = getAssociatedTokenAddressSync(dealMint, clientPk); 261 | const executorDealTa = getAssociatedTokenAddressSync(dealMint, executorPk); 262 | const checkerDealTa = checkerKey ? getAssociatedTokenAddressSync(dealMint, checkerKey) : executorDealTa; 263 | 264 | return dealContractProgram.methods.finish() 265 | .accountsStrict({ 266 | checkerDealTa, 267 | initializer, 268 | client: clientPk, 269 | executor: executorPk, 270 | checker: checkerKey ? checkerKey : initializer, 271 | payer: payerPk, 272 | 273 | clientHolderTa: getAssociatedTokenAddressSync(HOLDER_MINT, clientPk), 274 | 275 | dealMint, 276 | clientBondMint: clientBond ? clientBond.mint : dealMint, 277 | executorBondMint: executorBond ? executorBond.mint : dealMint, 278 | 279 | clientBondTa: clientBond ? getAssociatedTokenAddressSync(clientBond.mint, clientPk) : clientDealTa, 280 | 281 | dealStateClientBondTa: clientBond ? getAssociatedTokenAddressSync(clientBond.mint, dealState, true) : dealStateDealTa, 282 | dealStateDealTa, 283 | dealStateExecutorBondTa: executorBond ? getAssociatedTokenAddressSync(clientBond.mint, dealState, true) : dealStateDealTa, 284 | dealStateHolderTa: holderMode ? getAssociatedTokenAddressSync(HOLDER_MINT, dealState, true) : dealStateDealTa, 285 | 286 | executorBondTa: executorBond ? getAssociatedTokenAddressSync(executorBond.mint, executorPk) : executorDealTa, 287 | executorDealTa, 288 | 289 | dealState, 290 | 291 | holderMint: HOLDER_MINT, 292 | serviceFee: SERVICE_FEE_OWNER, 293 | associatedTokenProgram: ASSOCIATED_PROGRAM_ID, 294 | tokenProgram: TOKEN_PROGRAM_ID, 295 | systemProgram: anchor.web3.SystemProgram.programId, 296 | }).preInstructions([getTotalComputeIxs(400000)[0]]) 297 | } -------------------------------------------------------------------------------- /tests/deal_contract.ts: -------------------------------------------------------------------------------- 1 | import * as anchor from "@coral-xyz/anchor"; 2 | import { Program, AnchorProvider, IdlTypes, BN } from "@coral-xyz/anchor"; 3 | import { PublicKey, Keypair, Signer, SystemProgram, Transaction, Commitment, AddressLookupTableAccount, AddressLookupTableProgram, VersionedTransaction, VersionedMessage } from '@solana/web3.js'; 4 | import { TOKEN_PROGRAM_ID, createMint, createAccount, mintTo, getAccount, DEFAULT_ACCOUNT_STATE_SIZE, getAssociatedTokenAddressSync } from "@solana/spl-token"; 5 | import { DealContract, IDL as DC_IDL } from "../target/types/deal_contract"; 6 | import { assert } from "chai"; 7 | import { v4 as uuid } from 'uuid' 8 | import { DEAL_CONTRACT_PROGRAM_ID, getCancelIx, getDealStatePk, getFinishIx, getInitializeIx, getTotalComputeIxs, HOLDER_MINT, SERVICE_FEE_MINT, SERVICE_FEE_MINT_KP, SERVICE_FEE_OWNER, SERVICE_FEE_TA, signAndSendIxs as signAndSendIxs, uuidTodealIdBuf } from "./client"; 9 | import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet"; 10 | import { ASSOCIATED_PROGRAM_ID } from "@coral-xyz/anchor/dist/cjs/utils/token"; 11 | import './keys'; 12 | import { checkerKp, clientKp, executorKp, holderMintKp, mintAuthorityKp, payerKp, serviceFeeMintKp, serviceKp } from "./keys"; 13 | import { seed } from "@coral-xyz/anchor/dist/cjs/idl"; 14 | 15 | const ADDRESS_LOOKUP_TABLE_ADDRESS: PublicKey = new PublicKey("9479m8V6EuvFKPC812s8BZ3g3hD5Ru63hkR23Y7DLvEs"); 16 | let ADDRESS_LOOKUP_TABLE_ACCOUNT: AddressLookupTableAccount | undefined = undefined; 17 | async function getAddressLookupTable(pubkey?: PublicKey) { 18 | if (ADDRESS_LOOKUP_TABLE_ACCOUNT === undefined) { 19 | if (!pubkey) { throw new Error("addressLookupTable pubkey hasn't been passed")} 20 | const alt = await conn.getAddressLookupTable(pubkey); 21 | if (!alt.value) { throw new Error("addressLookupTable hasn't been properly created")} 22 | console.log(`ALT: ${pubkey.toString()}`); 23 | ADDRESS_LOOKUP_TABLE_ACCOUNT = alt.value as AddressLookupTableAccount 24 | } 25 | return ADDRESS_LOOKUP_TABLE_ACCOUNT as AddressLookupTableAccount 26 | } 27 | 28 | console.log(` 29 | payerKp: ${payerKp.publicKey.toString()} 30 | clientKp: ${clientKp.publicKey.toString()} 31 | executorKp: ${executorKp.publicKey.toString()} 32 | checkerKp: ${checkerKp.publicKey.toString()} 33 | mintAuthorityKp: ${mintAuthorityKp.publicKey.toString()} 34 | serviceKp: ${serviceKp.publicKey.toString()} 35 | serviceFeeMintKp: ${serviceFeeMintKp.publicKey.toString()} 36 | holderMintKp: ${holderMintKp.publicKey.toString()} 37 | `) 38 | 39 | const COMMITMENT = 'confirmed' 40 | 41 | const PROGRAM_ID = new PublicKey("GKNkN4uDJWmidEC9h5Q9GQXNg48Go6q5bdnkDj6bSopz") 42 | 43 | const conn = new anchor.web3.Connection("http://0.0.0.0:8899", {commitment: COMMITMENT}); 44 | const wallet = NodeWallet.local(); 45 | 46 | const confirmOptions = {commitment: COMMITMENT as Commitment, skipPreflight: true}; 47 | 48 | const provider = new anchor.AnchorProvider(conn, wallet, { commitment: COMMITMENT, skipPreflight: true }); 49 | // console.log(`provider: ${JSON.stringify(provider)}`) 50 | anchor.setProvider(provider) 51 | const program = new Program( DC_IDL as anchor.Idl, PROGRAM_ID, provider) as Program; 52 | 53 | 54 | describe("🤖 Tests Contractus smart-contract", () => { 55 | let addressLookupTablePk: PublicKey; 56 | it("create addressLookupTable", async()=>{ 57 | const alt = await conn.getAccountInfo(new PublicKey(ADDRESS_LOOKUP_TABLE_ADDRESS)); 58 | if (alt.data.length > 0) { 59 | getAddressLookupTable(ADDRESS_LOOKUP_TABLE_ADDRESS); 60 | return 61 | } 62 | 63 | const [ix, pk] = AddressLookupTableProgram.createLookupTable({ 64 | authority: payerKp.publicKey, 65 | payer: payerKp.publicKey, 66 | recentSlot: (await conn.getSlot()) 67 | }); 68 | 69 | addressLookupTablePk = pk; 70 | 71 | const tx = new Transaction(); 72 | tx.add(ix); 73 | tx.recentBlockhash = (await conn.getLatestBlockhash()).blockhash; 74 | tx.feePayer = payerKp.publicKey; 75 | tx.sign(payerKp); 76 | const createTxSig = await conn.sendTransaction(tx, [payerKp], {skipPreflight: true}) 77 | await conn.confirmTransaction(createTxSig, COMMITMENT); 78 | }) 79 | 80 | it("extendLookupTable", async() => { 81 | if (!addressLookupTablePk) { addressLookupTablePk = ADDRESS_LOOKUP_TABLE_ADDRESS; return } 82 | const extendInstruction = AddressLookupTableProgram.extendLookupTable({ 83 | payer: payerKp.publicKey, 84 | authority: payerKp.publicKey, 85 | lookupTable: addressLookupTablePk, 86 | addresses: [ 87 | DEAL_CONTRACT_PROGRAM_ID, 88 | HOLDER_MINT, 89 | SERVICE_FEE_MINT, 90 | SERVICE_FEE_OWNER, 91 | SERVICE_FEE_TA, 92 | SystemProgram.programId, 93 | TOKEN_PROGRAM_ID, 94 | ASSOCIATED_PROGRAM_ID, 95 | clientKp.publicKey, 96 | executorKp.publicKey, 97 | ], 98 | }); 99 | 100 | const tx = new Transaction(); 101 | tx.add(extendInstruction); 102 | tx.recentBlockhash = (await provider.connection.getLatestBlockhash()).blockhash; 103 | tx.feePayer = payerKp.publicKey; 104 | tx.sign(payerKp); 105 | console.log("We have to wait until extendLookupTable transaction is finalized..."); 106 | const extendTxSig = await conn.sendTransaction(tx, [payerKp], {skipPreflight: true}) 107 | await conn.confirmTransaction(extendTxSig, "finalized"); 108 | 109 | await getAddressLookupTable(addressLookupTablePk); 110 | }) 111 | 112 | 113 | it("transfer SOL from payer to another accounts", async()=>{ 114 | await provider.sendAndConfirm((() => { 115 | const tx = new Transaction(); 116 | tx.add(SystemProgram.transfer({fromPubkey: payerKp.publicKey,toPubkey: clientKp.publicKey,lamports: 1000000000,})); 117 | tx.add(SystemProgram.transfer({fromPubkey: payerKp.publicKey,toPubkey: executorKp.publicKey,lamports: 1000000000,})); 118 | tx.add(SystemProgram.transfer({fromPubkey: payerKp.publicKey,toPubkey: mintAuthorityKp.publicKey,lamports: 1000000000,})); 119 | tx.add(SystemProgram.transfer({fromPubkey: payerKp.publicKey,toPubkey: serviceKp.publicKey,lamports: 1000000000,})); 120 | tx.add(SystemProgram.transfer({fromPubkey: payerKp.publicKey,toPubkey: checkerKp.publicKey,lamports: 1000000000,})); 121 | return tx; 122 | })(), [payerKp]) 123 | }) 124 | 125 | it("create serviceFee token", async() => { 126 | await createMint( 127 | provider.connection, 128 | payerKp, 129 | mintAuthorityKp.publicKey, 130 | null, 131 | 0, 132 | serviceFeeMintKp, 133 | {commitment: COMMITMENT} 134 | ); 135 | await createAccount(provider.connection, payerKp, serviceFeeMintKp.publicKey, serviceKp.publicKey, null, confirmOptions, TOKEN_PROGRAM_ID); 136 | }) 137 | 138 | it("create `holder` token", async() => { 139 | await createMint( 140 | provider.connection, 141 | payerKp, 142 | mintAuthorityKp.publicKey, 143 | null, 144 | 0, 145 | holderMintKp, 146 | {commitment: COMMITMENT} 147 | ); 148 | }) 149 | 150 | describe("👽️ Deals with third party checker (no performance bond)", ()=> { 151 | const clientDealTokenBalance = 10000; 152 | const otherTokenBalance = 500; 153 | 154 | let dealMint: PublicKey; 155 | 156 | let clientDealTa: PublicKey; 157 | let clientHolderTa: PublicKey; 158 | let executorDealTa: PublicKey; 159 | 160 | const createDeal = async ({dealId, amount, serviceFee, withChecker, clientBond, executorBond, 161 | holderMode, signers, executor, client}: { 162 | dealId: string | Buffer, 163 | amount: number, 164 | serviceFee: { 165 | amount: number, 166 | mint?: PublicKey 167 | }, 168 | signers: Signer[], 169 | deadline?: number, 170 | withChecker?: { 171 | checkerFee: BN | number, 172 | checkerKey: PublicKey 173 | }, 174 | clientBond?: IdlTypes["Bond"], 175 | executorBond?: IdlTypes["Bond"], 176 | holderMode?: boolean, 177 | executor?: PublicKey, 178 | client?: PublicKey, 179 | }) => { 180 | const instruction = (await getInitializeIx({ 181 | dealContractProgram: program, 182 | dealId, 183 | amount, 184 | serviceFee, 185 | clientPk: client ? client : clientKp.publicKey, 186 | executorPk: executor ? executor : executorKp.publicKey, 187 | payerPk: payerKp.publicKey, 188 | dealMint, 189 | holderMode: !!holderMode ? holderMode : false, 190 | withChecker: !!withChecker ? { 191 | checkerKey: withChecker.checkerKey, 192 | checkerFee: new BN(withChecker.checkerFee) 193 | } : undefined, 194 | clientBond, 195 | executorBond 196 | })).instruction(); 197 | return await signAndSendIxs(conn, [getTotalComputeIxs(400000)[0], await instruction], signers, payerKp, [await getAddressLookupTable()]) 198 | }; 199 | 200 | let clientServiceFeeTa: PublicKey; 201 | let serviceServiceFeeTa: PublicKey; 202 | 203 | before( async()=>{ 204 | dealMint = await createMint( 205 | provider.connection, 206 | payerKp, 207 | mintAuthorityKp.publicKey, 208 | null, 209 | 0, 210 | Keypair.generate(), 211 | {commitment: COMMITMENT} 212 | ); 213 | console.log(`dealMint: ${dealMint.toString()}`); 214 | 215 | clientDealTa = getAssociatedTokenAddressSync(dealMint, clientKp.publicKey); 216 | executorDealTa = getAssociatedTokenAddressSync(dealMint, executorKp.publicKey); 217 | clientHolderTa = getAssociatedTokenAddressSync(HOLDER_MINT, clientKp.publicKey); 218 | 219 | clientServiceFeeTa = await createAccount(provider.connection, payerKp, serviceFeeMintKp.publicKey, clientKp.publicKey, undefined, confirmOptions, TOKEN_PROGRAM_ID); 220 | clientDealTa = await createAccount(provider.connection, payerKp, dealMint, clientKp.publicKey, undefined, confirmOptions, TOKEN_PROGRAM_ID); 221 | clientHolderTa = await createAccount(provider.connection, payerKp, HOLDER_MINT, clientKp.publicKey, undefined, confirmOptions, TOKEN_PROGRAM_ID); 222 | 223 | const mintDeal = mintTo(provider.connection, mintAuthorityKp, dealMint, clientDealTa, mintAuthorityKp.publicKey, clientDealTokenBalance, undefined, confirmOptions, TOKEN_PROGRAM_ID); 224 | const mintHolder = mintTo(provider.connection, mintAuthorityKp, HOLDER_MINT, clientHolderTa, mintAuthorityKp.publicKey, 1000000000, undefined, confirmOptions, TOKEN_PROGRAM_ID); 225 | await Promise.all([mintDeal, mintHolder]); 226 | }) 227 | 228 | it("Validating state", async () => { 229 | getAccount(provider.connection,clientDealTa).then(r=>{ 230 | assert.ok(r.mint.toBase58() === dealMint.toBase58(), "invalid client dealMint") 231 | assert.ok(r.amount.toString() == clientDealTokenBalance.toString(), "invalid client dealAmount") 232 | }); 233 | getAccount(provider.connection,clientHolderTa).then(r=>{ 234 | assert.ok(r.mint.toBase58() === HOLDER_MINT.toBase58(), "invalid client clientHolderTa.mint") 235 | assert.ok(r.amount.toString() != "0", "invalid client clientHolderTa.amount") 236 | }); 237 | }); 238 | 239 | it("Create deal", async () => { 240 | const dealId = uuidTodealIdBuf(uuid()) 241 | const checkerFee = 100 242 | const amount = 1000 243 | const serviceFee = 50 244 | 245 | const clientDealTaData = await conn.getTokenAccountBalance(clientDealTa, COMMITMENT); 246 | const clientHolderTaData = await conn.getTokenAccountBalance(clientHolderTa, COMMITMENT); 247 | 248 | await createDeal({ 249 | dealId, 250 | amount, 251 | serviceFee: { 252 | amount: serviceFee, 253 | mint: dealMint 254 | }, 255 | holderMode: false, 256 | withChecker: { 257 | checkerFee: new BN(checkerFee), 258 | checkerKey: checkerKp.publicKey 259 | }, 260 | signers: [clientKp, executorKp, checkerKp, payerKp] 261 | }); 262 | 263 | const dealStatePk = getDealStatePk(dealId, clientKp.publicKey, executorKp.publicKey)[0]; 264 | const dealStateDealTa = getAssociatedTokenAddressSync(dealMint, dealStatePk, true); 265 | const dealStateDealTaInfo = await getAccount(provider.connection, dealStateDealTa ); 266 | 267 | serviceServiceFeeTa = getAssociatedTokenAddressSync(dealMint, SERVICE_FEE_OWNER); 268 | 269 | const dealStateData = await program.account.dealState.fetch(dealStatePk, "processed"); 270 | const serviceFeeTaInfo = await getAccount(provider.connection, serviceServiceFeeTa ); 271 | 272 | assert.ok(serviceFeeTaInfo.amount.toString() == serviceFee.toString(), 273 | `invalid serviceFee: expected ${serviceFee.toString()}. got ${serviceFeeTaInfo.amount.toString()}`) 274 | assert.ok(dealStateData.amount.toString() == amount.toString(), 275 | `invalid dealStateData.amount: expected ${amount.toString()}. got ${dealStateData.amount.toString()}`) 276 | assert.ok(dealStateData.clientKey.toString() == clientKp.publicKey.toString(), 277 | `dealStateData.clientkey: expected: ${clientKp.publicKey}. got ${dealStateData.clientKey.toString()}`) 278 | assert.ok(dealStateData.executorKey.toString() == executorKp.publicKey.toString(), 279 | `dealStateData.executorkey: expected: ${executorKp.publicKey}. got ${dealStateData.executorKey.toString()}`) 280 | assert.ok(dealStateDealTaInfo.amount.toString() == (amount + checkerFee).toString(), 281 | `dealStateDealTaInfo.amount: expected ${(amount + checkerFee).toString()}. got ${dealStateDealTaInfo.amount.toString()}`) 282 | }); 283 | 284 | it("Try recreate deal with same ID", async () => { 285 | const dealId = uuidTodealIdBuf(uuid()) 286 | const amount = 1000 287 | const serviceFee = 50 288 | 289 | serviceServiceFeeTa = getAssociatedTokenAddressSync(dealMint, SERVICE_FEE_OWNER); 290 | 291 | let serviceFeeAmountBefore; 292 | serviceFeeAmountBefore = (await getAccount(provider.connection, serviceServiceFeeTa, "processed")).amount 293 | 294 | const sig = await createDeal({ 295 | dealId, 296 | amount, 297 | serviceFee: { 298 | amount: serviceFee, 299 | mint: dealMint 300 | }, 301 | signers: [clientKp, executorKp, payerKp] 302 | }); 303 | 304 | 305 | const dealStatePk = getDealStatePk(dealId, clientKp.publicKey, executorKp.publicKey)[0]; 306 | const dealStateDealTa = getAssociatedTokenAddressSync(dealMint, dealStatePk, true); 307 | const dealStateDealTaInfo = await getAccount(provider.connection, dealStateDealTa ); 308 | const dealStateData = await program.account.dealState.fetch(dealStatePk, "processed"); 309 | 310 | const serviceFeeAmountAfter = (await getAccount(provider.connection, serviceServiceFeeTa, "processed" )).amount 311 | const serviceFeePaid = serviceFeeAmountAfter - serviceFeeAmountBefore; 312 | 313 | try { 314 | assert.ok(serviceFeePaid.toString() == serviceFee.toString(), `invalid serviceFeePaid` ); 315 | assert.ok(dealStateData.amount.toString() == amount.toString(), 316 | `invalid dealStateData.amount: expected ${amount.toString()}. got ${dealStateData.amount.toString()}`) 317 | assert.ok(dealStateData.clientKey.toString() == clientKp.publicKey.toString(), 318 | `dealStateData.clientkey: expected: ${clientKp.publicKey}. got ${dealStateData.clientKey.toString()}`) 319 | assert.ok(dealStateData.executorKey.toString() == executorKp.publicKey.toString(), 320 | `dealStateData.executorkey: expected: ${executorKp.publicKey}. got ${dealStateData.executorKey.toString()}`) 321 | assert.ok(dealStateDealTaInfo.amount.toString() == amount .toString(), 322 | `dealStateDealTaInfo.amount: expected ${amount.toString()}. got ${dealStateDealTaInfo.amount.toString()}`) 323 | } catch (e) { 324 | console.log(`failed assertion tx signature: ${sig}`); 325 | throw e 326 | } 327 | 328 | // Try call Init again 329 | try { 330 | await createDeal({ 331 | dealId, 332 | amount, 333 | serviceFee: { 334 | amount: serviceFee, 335 | mint: dealMint 336 | }, 337 | signers: [clientKp, executorKp, payerKp] 338 | }); 339 | assert.ok(false) 340 | } catch { 341 | assert.ok(true) 342 | } 343 | }); 344 | 345 | it("Create deal and finish with checker", async () => { 346 | const dealId = uuidTodealIdBuf(uuid()) 347 | const checkerFee = 100 348 | const amount = 1000 349 | const serviceFee = 50 350 | 351 | const clientDealTaInfoBefore = (await getAccount(provider.connection, clientDealTa, "processed" )); 352 | const executorDealTaInfoBefore = (await getAccount(provider.connection, executorDealTa, "processed" )); 353 | const checkerDealTaAmountBefore = (await getAccount(provider.connection, executorDealTa, "processed" )).amount | BigInt(0); 354 | 355 | await createDeal({ 356 | dealId, 357 | amount, 358 | serviceFee: { 359 | amount: serviceFee, 360 | mint: dealMint 361 | }, 362 | signers: [clientKp, executorKp, checkerKp, payerKp], 363 | withChecker: { 364 | checkerKey: checkerKp.publicKey, 365 | checkerFee: new BN(checkerFee) 366 | } 367 | }); 368 | 369 | const dealStatePk = getDealStatePk(dealId, clientKp.publicKey, executorKp.publicKey)[0]; 370 | const dealStateDealTa = getAssociatedTokenAddressSync(dealMint, dealStatePk, true); 371 | const dealStateDealTaInfoBefore = await getAccount(provider.connection, dealStateDealTa, "processed" ); 372 | assert.ok(Number(dealStateDealTaInfoBefore.amount) == amount + checkerFee, 373 | `invalid dealStateDealTaInfoBefore.amount. expected ${amount + checkerFee} got ${dealStateDealTaInfoBefore.amount}`) 374 | 375 | const dealStateHolderTa = getAssociatedTokenAddressSync(HOLDER_MINT, dealStatePk, true); 376 | 377 | const checkerDealTa = getAssociatedTokenAddressSync(dealMint, checkerKp.publicKey); 378 | 379 | const instruction = (await getFinishIx({ 380 | initializer: checkerKp.publicKey, 381 | dealMint, 382 | clientPk: clientKp.publicKey, 383 | dealContractProgram: program, 384 | dealId, 385 | executorPk: executorKp.publicKey, 386 | checkerKey: checkerKp.publicKey, 387 | payerPk: payerKp.publicKey, 388 | })).instruction() 389 | await signAndSendIxs(conn, [await instruction], [checkerKp, payerKp], payerKp, [await getAddressLookupTable()]) 390 | 391 | const dealStateDealTaInfoAfter = await conn.getAccountInfo(dealStateDealTa, "processed" ); 392 | assert.ok(dealStateDealTaInfoAfter == null, `dealStateDealTaInfoAfter hadn't been closed`) 393 | 394 | const clientDealTaInfo = await getAccount(provider.connection, clientDealTa, "processed" ) 395 | assert.ok((Number(clientDealTaInfoBefore.amount) - checkerFee - serviceFee - amount).toString() == clientDealTaInfo.amount.toString(), 396 | `invalid clientDealTaInfo.amount. expected ${(Number(clientDealTaInfoBefore.amount) - checkerFee - serviceFee - amount)} got ${clientDealTaInfo.amount}`) 397 | 398 | const executorDealTaInfoAfter = await getAccount(provider.connection, executorDealTa, "processed" ); 399 | assert.ok(executorDealTaInfoAfter.amount.toString() == (Number(executorDealTaInfoBefore.amount) + amount).toString(), 400 | `invalid executorDealTaInfo.amount. expected ${Number(executorDealTaInfoBefore.amount) + amount} got ${executorDealTaInfoAfter.amount}`) 401 | 402 | const checkerDealTaInfo = await getAccount(provider.connection, checkerDealTa, "processed" ); 403 | assert.ok(checkerDealTaInfo.amount.toString() == (Number(checkerDealTaAmountBefore) + checkerFee).toString(), 404 | `invalid checkerDealTaInfo.amount. expected ${Number(checkerDealTaAmountBefore) + checkerFee} got ${checkerDealTaInfo.amount}`) 405 | }); 406 | 407 | it("Create deal and cancel as checker", async () => { 408 | const dealId = uuidTodealIdBuf(uuid()) 409 | const checkerFee = 100 410 | const amount = 1000 411 | const serviceFee = 50 412 | 413 | await createDeal({ 414 | dealId, 415 | amount, 416 | serviceFee: { 417 | amount: serviceFee, 418 | mint: dealMint 419 | }, 420 | signers: [clientKp, executorKp, checkerKp, payerKp], 421 | withChecker: { 422 | checkerKey: checkerKp.publicKey, 423 | checkerFee: new BN(checkerFee) 424 | }, 425 | deadline: 1 426 | }); 427 | 428 | const dealStatePk = getDealStatePk(dealId, clientKp.publicKey, executorKp.publicKey)[0]; 429 | const dealStateDealTa = getAssociatedTokenAddressSync(dealMint, dealStatePk, true); 430 | const dealStateDealTaInfo = await getAccount(provider.connection, dealStateDealTa ); 431 | 432 | const dealStateData = await program.account.dealState.fetch(dealStatePk); 433 | 434 | const dealStateDealTaData = await getAccount(provider.connection, dealStateDealTa, "processed"); 435 | 436 | const clientDealTaInfoBefore = await getAccount(provider.connection, clientDealTa, "processed" ); 437 | 438 | assert.ok(dealStateDealTaData.amount.toString() == (amount + checkerFee).toString(), 439 | `invalid dealStateDealTaData.amount. expected ${amount + checkerFee} got ${dealStateDealTaData.amount}`) 440 | assert.ok(dealStateData.checker.checkerFee.toString() == new anchor.BN(checkerFee).toString(), 441 | `invalid dealStateData.checker.checkerFee. expected ${checkerFee} got ${dealStateData.checker.checkerFee}`) 442 | assert.ok(dealStateData.amount.toNumber().toString() == amount.toString(), 443 | `invalid dealStateData.amount. expected ${amount} got ${dealStateData.amount}`) 444 | assert.ok(dealStateData.clientKey.toBase58() == clientKp.publicKey.toBase58(), 445 | `invalid dealStateData.clientKey. expected ${clientKp.publicKey} got ${dealStateData.clientKey}`) 446 | assert.ok(dealStateData.executorKey.toBase58() == executorKp.publicKey.toBase58(), 447 | `invalid dealStateData.executorKey. expected ${executorKp.publicKey} got ${dealStateData.executorKey}`) 448 | 449 | const instruction = (await getCancelIx({ 450 | initializer: checkerKp.publicKey, 451 | dealMint, 452 | clientPk: clientKp.publicKey, 453 | dealContractProgram: program, 454 | dealId, 455 | executorPk: executorKp.publicKey, 456 | checkerKey: checkerKp.publicKey, 457 | payerPk: payerKp.publicKey 458 | })).instruction(); 459 | await signAndSendIxs(conn, [await instruction], [payerKp, checkerKp], checkerKp, [await getAddressLookupTable()]) 460 | 461 | const clientDealTaInfo = await getAccount(provider.connection, clientDealTa, "processed"); 462 | assert.ok((Number(clientDealTaInfoBefore.amount) + Number(amount)).toString() == clientDealTaInfo.amount.toString(), 463 | `invalid clientDealTaInfo.amount. expected ${(Number(clientDealTaInfoBefore.amount) + Number(amount + checkerFee)).toString()} got ${clientDealTaInfo.amount}`) 464 | }); 465 | 466 | it("Try create deal with the same executor and client", async () => { 467 | createDeal( 468 | { 469 | dealId: uuid(), 470 | amount: 1000, 471 | serviceFee: { 472 | amount: 100, 473 | mint: dealMint 474 | }, 475 | client: clientKp.publicKey, 476 | executor: clientKp.publicKey, 477 | holderMode: false, 478 | signers: [clientKp] 479 | }).then(() => { 480 | assert.ok(false) 481 | }).catch((error) => { 482 | assert.ok(error.error.errorCode.code == "ConstraintRaw") 483 | }) 484 | }) 485 | 486 | it("Try create deal with the zero fee (holder mode off)", async () => { 487 | createDeal({ 488 | dealId: uuid(), 489 | amount: 1000, 490 | serviceFee: { 491 | amount: 0, 492 | mint: dealMint 493 | }, 494 | signers: [clientKp, executorKp] 495 | }).then(() => { 496 | assert.ok(false) 497 | }).catch((error) => { 498 | // TODO: - Add validation by errorCode 499 | assert.ok(true) 500 | }) 501 | }) 502 | 503 | it("Try create deal with the zero fee (holder mode on, but not fund)", async () => { 504 | try { 505 | await createDeal({ 506 | dealId: uuid(), 507 | amount: 1000, 508 | serviceFee: { 509 | amount: 0, 510 | mint: dealMint 511 | }, 512 | withChecker: { 513 | checkerFee: 0, 514 | checkerKey: checkerKp.publicKey 515 | }, 516 | holderMode: true, 517 | signers: [clientKp, executorKp, checkerKp] 518 | }) 519 | assert.ok(false) 520 | } catch(error) { 521 | // TODO: - Add validation by errorCode 522 | assert.ok(true) 523 | } 524 | }) 525 | 526 | it("Create deal with zero amount, fee and service fee with custom token", async () => { 527 | try { 528 | await createDeal({ 529 | dealId: uuid(), 530 | amount: 0, 531 | serviceFee: { 532 | amount: 0, 533 | mint: dealMint 534 | }, 535 | signers: [clientKp, executorKp, checkerKp, payerKp], 536 | holderMode: false 537 | }) 538 | assert.ok(false) 539 | } catch(error) { 540 | // TODO: - Add validation by errorCode 541 | assert.ok(true) 542 | } 543 | }) 544 | 545 | it("Create deal with zero service fee with custom token", async () => { 546 | try { 547 | await createDeal({ 548 | dealId: uuid(), 549 | amount: 1000, 550 | serviceFee: { 551 | amount: 0, 552 | mint: dealMint 553 | }, 554 | signers: [clientKp, executorKp, checkerKp, payerKp], 555 | holderMode: false 556 | }) 557 | assert.ok(false) 558 | } catch(error) { 559 | // TODO: - Add validation by errorCode 560 | assert.ok(true) 561 | } 562 | }) 563 | }) 564 | 565 | // describe("👻 Deals with performance bond (no checker)", ()=> { 566 | // const amount = 1000; 567 | // const service_fee = 50; 568 | // const clientTokenBalance = 10000; 569 | // const otherTokenBalance = 500; 570 | // const serviceFeeTokenBalance = 0; 571 | // const bondTokenBalance = 0; 572 | 573 | // // const dealAccount = anchor.web3.Keypair.generate(); 574 | // const payer = anchor.web3.Keypair.generate(); 575 | // const mintAuthority = anchor.web3.Keypair.generate(); 576 | 577 | // const clientAccount = anchor.web3.Keypair.generate(); 578 | // const executorAccount = anchor.web3.Keypair.generate(); 579 | // const checkerAccount = anchor.web3.Keypair.generate(); 580 | // const serviceFeeAccount = anchor.web3.Keypair.generate(); 581 | // const serviceFeeMintAuthority = anchor.web3.Keypair.generate(); 582 | // const bondMintAuthority = anchor.web3.Keypair.generate(); 583 | // const serviceFeeMintKeypair: Keypair = serviceFeeMintKp; 584 | 585 | // var mint; 586 | // var mintBond; 587 | 588 | // var clientTa; 589 | // var executorTa; 590 | // var checkerTa; 591 | // var serviceFeeTa; 592 | 593 | // var bondClientTa; 594 | // var bondExecutorTa; 595 | 596 | // var serviceFeeMint; 597 | // var clientServiceTa; 598 | // var serviceFeeServiceTa; 599 | 600 | // const createDeal = async ( 601 | // dealId, 602 | // amount, 603 | // clientBondAmount, 604 | // executorBondAmount, 605 | // serviceFee, 606 | // clientAccount, 607 | // executorAccount, 608 | // payer, 609 | // serviceFeeTa, 610 | // clientTa, 611 | // clientServiceTa, 612 | // executorTa, 613 | // clientBondTa, 614 | // clientBondMint, 615 | // executorBondTa, 616 | // executorBondMint, 617 | // mint, 618 | // holderMint, 619 | // holderMode, 620 | // deadline 621 | // ) => { 622 | // dealId = uuidTodealIdBuf(dealId); 623 | 624 | // (await getInitializeIx({ 625 | // dealContractProgram: program, 626 | // dealId, 627 | // amount, 628 | // serviceFee, 629 | // clientPk: clientAccount ? clientAccount : clientKp.publicKey, 630 | // executorPk: executorAccount ? executorAccount : executorKp.publicKey, 631 | // payerPk: payerKp.publicKey, 632 | // dealMint: mint, 633 | // holderMode, 634 | // clientBond: { 635 | // mint: clientBondMint, 636 | // amount: clientBondAmount, 637 | // }, 638 | // executorBond: { 639 | // mint: executorBondMint, 640 | // amount: executorBondAmount 641 | // } 642 | // })).preInstructions([getTotalComputeIxs(800000)[0]]).rpc(); 643 | 644 | // const dealStatePk = getDealStatePk(dealId, clientAccount, executorAccount, DEAL_CONTRACT_PROGRAM_ID)[0]; 645 | // const dealStateDealTa = getAssociatedTokenAddressSync(mint, dealStatePk, true); 646 | // const dealStateExecutorBondTa = getAssociatedTokenAddressSync(executorBondMint, dealStatePk, true); 647 | // const dealStateClientBondTa = getAssociatedTokenAddressSync(clientAccount, dealStatePk, true); 648 | 649 | // return { 650 | // dealId, 651 | // dealMint: mint, 652 | // dealStateDealTa, 653 | // dealStatePk, 654 | // dealStateExecutorBondTa, 655 | // dealStateClientBondTa, 656 | // clientBondMint, 657 | // executorBondMint 658 | // } 659 | // } 660 | 661 | // before(async () => { 662 | // await provider.connection.confirmTransaction( 663 | // await provider.connection.requestAirdrop(payer.publicKey, 2000000000), 664 | // "processed" 665 | // ); 666 | 667 | // await provider.sendAndConfirm((() => { 668 | // const tx = new Transaction(); 669 | // tx.add( 670 | // SystemProgram.transfer({ 671 | // fromPubkey: payer.publicKey, 672 | // toPubkey: clientAccount.publicKey, 673 | // lamports: 100000000, 674 | // }) 675 | // ); 676 | // return tx; 677 | // })(), [payer]) 678 | // const accountInfo = await provider.connection.getAccountInfo( 679 | // clientAccount.publicKey 680 | // ) 681 | // assert.ok(accountInfo.lamports == 100000000) 682 | // mint = await createMint( 683 | // provider.connection, 684 | // payer, 685 | // mintAuthority.publicKey, 686 | // null, 687 | // 0); 688 | 689 | // mintBond = await createMint( 690 | // provider.connection, 691 | // payer, 692 | // bondMintAuthority.publicKey, 693 | // null, 694 | // 0); 695 | 696 | // try { 697 | // serviceFeeMint = await createMint( 698 | // provider.connection, 699 | // payer, 700 | // serviceFeeMintAuthority.publicKey, 701 | // null, 702 | // 0, 703 | // serviceFeeMintKeypair); 704 | // } catch { 705 | // serviceFeeMint = serviceFeeMintKeypair.publicKey 706 | // } 707 | 708 | 709 | // clientTa = await createAccount(provider.connection, payer, mint, clientAccount.publicKey, null, null, TOKEN_PROGRAM_ID); 710 | // executorTa = await createAccount(provider.connection, payer, mint, executorAccount.publicKey, null, null, TOKEN_PROGRAM_ID); 711 | // checkerTa = await createAccount(provider.connection, payer, mint, checkerAccount.publicKey, null, null, TOKEN_PROGRAM_ID); 712 | // serviceFeeTa = await createAccount(provider.connection, payer, mint, serviceFeeAccount.publicKey, null, null, TOKEN_PROGRAM_ID); 713 | 714 | // bondClientTa = await createAccount(provider.connection, payer, mintBond, clientAccount.publicKey, null, null, TOKEN_PROGRAM_ID); 715 | // bondExecutorTa = await createAccount(provider.connection, payer, mintBond, executorAccount.publicKey, null, null, TOKEN_PROGRAM_ID); 716 | 717 | // clientServiceTa = await createAccount(provider.connection, payer, serviceFeeMint, clientAccount.publicKey, null, null, TOKEN_PROGRAM_ID); 718 | // serviceFeeServiceTa = await createAccount(provider.connection, payer, serviceFeeMint, serviceFeeAccount.publicKey, null, null, TOKEN_PROGRAM_ID); 719 | 720 | // await mintTo(provider.connection, payer, mint, clientTa, mintAuthority.publicKey, clientTokenBalance, [mintAuthority]) 721 | // await mintTo(provider.connection, payer, mint, executorTa, mintAuthority.publicKey, otherTokenBalance, [mintAuthority]) 722 | // await mintTo(provider.connection, payer, mint, checkerTa, mintAuthority.publicKey, otherTokenBalance, [mintAuthority]) 723 | // await mintTo(provider.connection, payer, mint, serviceFeeTa, mintAuthority.publicKey, serviceFeeTokenBalance, [mintAuthority]) 724 | // }) 725 | 726 | // it("Validate state", async () => { 727 | 728 | // const clientTaInfo = await getAccount( 729 | // provider.connection, 730 | // clientTa 731 | // ) 732 | // const executorTaInfo = await getAccount( 733 | // provider.connection, 734 | // executorTa 735 | // ) 736 | // const checkerTaInfo = await getAccount( 737 | // provider.connection, 738 | // checkerTa 739 | // ) 740 | 741 | // assert.ok(clientTaInfo.mint.toBase58() == mint.toBase58()) 742 | // assert.ok(clientTaInfo.amount.toString() == clientTokenBalance.toString()) 743 | // assert.ok(executorTaInfo.amount.toString() == otherTokenBalance.toString()) 744 | // assert.ok(checkerTaInfo.amount.toString() == otherTokenBalance.toString()) 745 | // }); 746 | 747 | // it("Create deal with holder mode (no CTUS fund)", async () => { 748 | // try { 749 | // var data = await createDeal( 750 | // uuid(), 751 | // amount, 752 | // 0, 753 | // 0, 754 | // 0, 755 | // clientAccount, 756 | // executorAccount, 757 | // payer, 758 | // serviceFeeTa, 759 | // clientTa, 760 | // clientServiceTa, 761 | // executorTa, 762 | // bondClientTa, 763 | // mintBond, 764 | // bondExecutorTa, 765 | // mintBond, 766 | // mint, 767 | // serviceFeeMint, 768 | // true, 769 | // new Date().getTime() / 1000) 770 | 771 | // const state = await program.account.dealState.fetch(data.dealStatePk) 772 | // const serviceFeeTaInfo = await getAccount( 773 | // provider.connection, 774 | // serviceFeeTa 775 | // ) 776 | // assert.ok(serviceFeeTaInfo.amount.toString() == service_fee.toString()) 777 | // assert.ok(state.amount.toNumber().toString() == amount.toString()) 778 | // assert.ok(state.clientKey.toBase58() == clientAccount.publicKey.toBase58()) 779 | // assert.ok(state.executorKey.toBase58() == executorAccount.publicKey.toBase58()) 780 | // } catch(err) { 781 | // assert.ok(err.error.origin == "client_service_token_account") 782 | // } 783 | // }) 784 | 785 | // it("Create deal with executor bond", async () => { 786 | 787 | // await mintTo(provider.connection, payer, mintBond, bondExecutorTa, bondMintAuthority.publicKey, 100, [bondMintAuthority]) 788 | 789 | // let serviceFee = BigInt(100) 790 | // let executorBond = BigInt(56) 791 | // const serviceFeeTaInfo = await getAccount( 792 | // provider.connection, 793 | // serviceFeeTa 794 | // ) 795 | // var serviceAccountAmount = serviceFeeTaInfo.amount 796 | 797 | // try { 798 | // var data = await createDeal( 799 | // uuid(), 800 | // amount, 801 | // 0, 802 | // executorBond, 803 | // serviceFee, 804 | // clientAccount, 805 | // executorAccount, 806 | // payer, 807 | // serviceFeeTa, 808 | // clientTa, 809 | // clientServiceTa, 810 | // executorTa, 811 | // bondClientTa, 812 | // mintBond, 813 | // bondExecutorTa, 814 | // mintBond, 815 | // mint, 816 | // serviceFeeMint, 817 | // false, 818 | // new Date().getTime() / 1000) 819 | 820 | // const state = await program.account.dealState.fetch(data.dealStatePk) 821 | // const serviceFeeTaInfo = await getAccount( 822 | // provider.connection, 823 | // serviceFeeTa 824 | // ) 825 | // const executorBondTaInfo = await getAccount( 826 | // provider.connection, 827 | // data.dealStateExecutorBondTa 828 | // ) 829 | // assert.ok(serviceFeeTaInfo.amount.toString() == (serviceAccountAmount + serviceFee).toString()) 830 | // assert.ok(executorBondTaInfo.amount.toString() == executorBond.toString()) 831 | // assert.ok(state.amount.toNumber().toString() == amount.toString()) 832 | // assert.ok(state.clientKey.toBase58() == clientAccount.publicKey.toBase58()) 833 | // assert.ok(state.executorKey.toBase58() == executorAccount.publicKey.toBase58()) 834 | // } catch(err) { 835 | // console.log(err) 836 | // assert.ok(false) 837 | // } 838 | // }) 839 | 840 | // it("Try create deal twice", async () => { 841 | 842 | // await mintTo(provider.connection, payer, mintBond, bondExecutorTa, bondMintAuthority.publicKey, 100, [bondMintAuthority]) 843 | 844 | // let serviceFee = BigInt(100) 845 | // let executorBond = BigInt(56) 846 | // let dealId = uuid() 847 | // try { 848 | // await createDeal( 849 | // dealId, 850 | // amount, 851 | // 0, 852 | // executorBond, 853 | // serviceFee, 854 | // clientAccount, 855 | // executorAccount, 856 | // payer, 857 | // serviceFeeTa, 858 | // clientTa, 859 | // clientServiceTa, 860 | // executorTa, 861 | // bondClientTa, 862 | // mintBond, 863 | // bondExecutorTa, 864 | // mintBond, 865 | // mint, 866 | // serviceFeeMint, 867 | // false, 868 | // new Date().getTime() / 1000) 869 | 870 | // assert.ok(true) 871 | // await createDeal( 872 | // dealId, 873 | // amount, 874 | // 0, 875 | // executorBond, 876 | // serviceFee, 877 | // clientAccount, 878 | // executorAccount, 879 | // payer, 880 | // serviceFeeTa, 881 | // clientTa, 882 | // clientServiceTa, 883 | // executorTa, 884 | // bondClientTa, 885 | // mintBond, 886 | // bondExecutorTa, 887 | // mintBond, 888 | // mint, 889 | // serviceFeeMint, 890 | // false, 891 | // new Date().getTime() / 1000) 892 | // assert.ok(false) 893 | // } catch(err) { 894 | // assert.ok(err.error.origin == "deposit_account") 895 | // } 896 | // }) 897 | 898 | // it("Create deal with bond and try cancel", async () => { 899 | // await mintTo(provider.connection, payer, mintBond, bondExecutorTa, bondMintAuthority.publicKey, 100, [bondMintAuthority]) 900 | // let serviceFee = BigInt(100) 901 | // let executorBond = BigInt(56) 902 | 903 | // var executorTaInfo = await getAccount( 904 | // provider.connection, 905 | // executorTa 906 | // ) 907 | 908 | // try { 909 | // let data = await createDeal( 910 | // uuid(), 911 | // amount, 912 | // 0, 913 | // executorBond, 914 | // serviceFee, 915 | // clientAccount, 916 | // executorAccount, 917 | // payer, 918 | // serviceFeeTa, 919 | // clientTa, 920 | // clientServiceTa, 921 | // executorTa, 922 | // bondClientTa, 923 | // mintBond, 924 | // bondExecutorTa, 925 | // mintBond, 926 | // mint, 927 | // serviceFeeMint, 928 | // false, 929 | // (new Date().getTime() / 1000) + 1000 930 | // ) 931 | // executorTaInfo = await getAccount( 932 | // provider.connection, 933 | // executorTa 934 | // ) 935 | // try { 936 | // (await getCancelIx({ 937 | // dealContractProgram: program, 938 | // dealId: data.dealId, 939 | // clientPk: clientAccount.publicKey, 940 | // dealMint: data.dealMint, 941 | // executorPk: executorAccount.publicKey, 942 | // initializer: clientAccount.publicKey, 943 | // clientBondMint: data.clientBondMint, 944 | // executorBondMint: data.executorBondMint, 945 | // })).signers([clientAccount]).rpc() 946 | // } catch(error) { 947 | // assert.ok(error.error.errorCode.code == 'DeadlineNotCome') 948 | // } 949 | // } catch(error) { 950 | // console.log(error) 951 | // assert.ok(false) 952 | // } 953 | // }) 954 | 955 | // it("Create and finish deal with bond and deadline", async () => { 956 | 957 | // await mintTo(provider.connection, payer, mintBond, bondClientTa, bondMintAuthority.publicKey, 100, [bondMintAuthority]) 958 | // await mintTo(provider.connection, payer, mintBond, bondExecutorTa, bondMintAuthority.publicKey, 100, [bondMintAuthority]) 959 | 960 | // var bondClientTaInfo = await getAccount( 961 | // provider.connection, 962 | // bondClientTa 963 | // ) 964 | // var bondClientTokenAmountBefore = bondClientTaInfo.amount 965 | 966 | // var bondExecutorTaInfo = await getAccount( 967 | // provider.connection, 968 | // bondExecutorTa 969 | // ) 970 | // var bondExecutorTokenAmountBefore = bondExecutorTaInfo.amount 971 | 972 | // var clientTaInfo = await getAccount( 973 | // provider.connection, 974 | // clientTa 975 | // ) 976 | // var clientTokenAmountBefore = clientTaInfo.amount 977 | 978 | // let amount = BigInt(100) 979 | // let serviceFee = BigInt(100) 980 | // let executorBond = BigInt(56) 981 | // let clientBond = BigInt(40) 982 | // let data 983 | // let deadline = new Date().getTime() / 1000 984 | // try { 985 | // data = await createDeal( 986 | // uuid(), 987 | // amount, 988 | // clientBond, 989 | // executorBond, 990 | // serviceFee, 991 | // clientAccount, 992 | // executorAccount, 993 | // payer, 994 | // serviceFeeTa, 995 | // clientTa, 996 | // clientServiceTa, 997 | // executorTa, 998 | // bondClientTa, 999 | // mintBond, 1000 | // bondExecutorTa, 1001 | // mintBond, 1002 | // mint, 1003 | // serviceFeeMint, 1004 | // false, 1005 | // deadline 1006 | // ) 1007 | 1008 | // bondClientTaInfo = await getAccount( 1009 | // provider.connection, 1010 | // bondClientTa 1011 | // ) 1012 | // var bondClientTokenAmountAfter = bondClientTaInfo.amount 1013 | 1014 | // bondExecutorTaInfo = await getAccount( 1015 | // provider.connection, 1016 | // bondExecutorTa 1017 | // ) 1018 | // var bondExecutorTokenAmountAfter = bondExecutorTaInfo.amount 1019 | 1020 | // let depositBondExecutorTaInfo = await getAccount( 1021 | // provider.connection, 1022 | // data.executor_bond_vault_account_pda 1023 | // ) 1024 | 1025 | // let depositBondClientTaInfo = await getAccount( 1026 | // provider.connection, 1027 | // data.client_bond_vault_account_pda 1028 | // ) 1029 | 1030 | // clientTaInfo = await getAccount( 1031 | // provider.connection, 1032 | // clientTa 1033 | // ) 1034 | // var clientTokenAmountAfter = clientTaInfo.amount 1035 | 1036 | // assert.ok(bondExecutorTokenAmountAfter < bondExecutorTokenAmountBefore) 1037 | // assert.ok(clientTokenAmountAfter < clientTokenAmountBefore) 1038 | // assert.ok(bondClientTokenAmountAfter < bondClientTokenAmountBefore) 1039 | // assert.ok(depositBondExecutorTaInfo.amount == executorBond) 1040 | // assert.ok(depositBondClientTaInfo.amount == clientBond) 1041 | 1042 | // let before_deadline = new Date().getTime() / 1000 1043 | // assert.ok(before_deadline > deadline); 1044 | 1045 | // (await getCancelIx({ 1046 | // clientPk: clientAccount.publicKey, 1047 | // dealContractProgram: program, 1048 | // dealId: data.dealId, 1049 | // dealMint: data.dealMint, 1050 | // executorPk: executorAccount.publicKey, 1051 | // initializer: executorAccount.publicKey, 1052 | // checkerKey: checkerAccount.publicKey, 1053 | // clientBondMint: mintBond, 1054 | // executorBondMint: mintBond, 1055 | // })).signers([clientAccount]) 1056 | // .rpc() 1057 | 1058 | // bondClientTaInfo = await getAccount( 1059 | // provider.connection, 1060 | // bondClientTa 1061 | // ) 1062 | // var bondClientTokenAmountAfterCancel = bondClientTaInfo.amount 1063 | 1064 | // bondExecutorTaInfo = await getAccount( 1065 | // provider.connection, 1066 | // bondExecutorTa 1067 | // ) 1068 | // var bondExecutorTokenAmountAfterCancel = bondExecutorTaInfo.amount 1069 | 1070 | // clientTaInfo = await getAccount( 1071 | // provider.connection, 1072 | // clientTa 1073 | // ) 1074 | // var clientTokenAmountAfterCancel = clientTaInfo.amount 1075 | 1076 | // assert.ok(bondClientTokenAmountAfterCancel == bondClientTokenAmountBefore) 1077 | // assert.ok(bondExecutorTokenAmountAfterCancel == bondExecutorTokenAmountBefore) 1078 | // assert.ok(clientTokenAmountAfterCancel == clientTokenAmountBefore - serviceFee) 1079 | 1080 | // } catch(error) { 1081 | // assert.ok(false) 1082 | // } 1083 | // }) 1084 | // }) 1085 | }); 1086 | -------------------------------------------------------------------------------- /tests/keys/alt.json: -------------------------------------------------------------------------------- 1 | { 2 | "pubkey": "9479m8V6EuvFKPC812s8BZ3g3hD5Ru63hkR23Y7DLvEs", 3 | "account": { 4 | "lamports": 3507840, 5 | "data": [ 6 | "AQAAAP//////////BAAAAAAAAAAAARyxvIzR/85B2cdVkFHsssSszcC4gElAyqmEhXk3LHxXAADjlFk9kTaOIstJabg4si8fT5kO1heoThzDujZAT/B+/0s6AaHBl3vH+9pt079m7zExQOokrEiSgHeOWpF/eWwkSqXlHjOvHGKsHVxcxGAjsNRg9KqSgTwxo+4blcv2lrylcRR2jAfhttod4Ky011Oc7N2YzvZJ8yEyxneBIi0pV3T3+wwfCE6RX8ZrofzLOjvEu/H930WDNHCPxaOrSDbWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqYyXJY9OJInxuz0QKRSODYMLWhOZ2v8QhASOe9jb6fhZIXtYltGP7rI/OEqHlaclhdVbZBqGdNg+oSfXL5Oc6y444Yyv6c5YG/n4Jq1XYGXCzYMIQChIJBgSOfK6Xy359Q==", 7 | "base64" 8 | ], 9 | "owner": "AddressLookupTab1e1111111111111111111111111", 10 | "executable": false, 11 | "rentEpoch": 0 12 | } 13 | } -------------------------------------------------------------------------------- /tests/keys/checker.json: -------------------------------------------------------------------------------- 1 | [152,18,244,139,117,21,170,19,53,226,73,74,200,179,47,253,57,129,58,135,101,163,216,32,94,15,230,53,115,179,109,0,52,12,77,158,21,171,185,83,134,226,250,199,252,141,45,23,96,71,28,44,250,115,240,83,249,75,197,228,11,156,226,186] -------------------------------------------------------------------------------- /tests/keys/client.json: -------------------------------------------------------------------------------- 1 | [160,67,70,2,126,145,138,125,125,95,204,207,52,233,61,37,82,227,227,212,44,169,129,26,101,69,175,184,69,240,206,255,33,123,88,150,209,143,238,178,63,56,74,135,149,167,37,133,213,91,100,26,134,116,216,62,161,39,215,47,147,156,235,46] -------------------------------------------------------------------------------- /tests/keys/deal_contract.json: -------------------------------------------------------------------------------- 1 | [223,73,61,149,255,253,244,245,23,135,101,107,50,118,184,140,103,122,51,149,59,239,226,179,101,239,191,9,131,2,72,132,227,148,89,61,145,54,142,34,203,73,105,184,56,178,47,31,79,153,14,214,23,168,78,28,195,186,54,64,79,240,126,255] -------------------------------------------------------------------------------- /tests/keys/executor.json: -------------------------------------------------------------------------------- 1 | [100,70,102,163,9,113,235,72,91,16,234,112,218,191,74,91,210,210,200,173,178,24,60,173,24,105,33,86,240,206,80,17,56,225,140,175,233,206,88,27,249,248,38,173,87,96,101,194,205,131,8,64,40,72,36,24,18,57,242,186,95,45,249,245] -------------------------------------------------------------------------------- /tests/keys/holder_mint.json: -------------------------------------------------------------------------------- 1 | [78,123,95,20,86,137,160,49,39,247,178,17,221,137,71,160,54,165,71,75,52,183,188,238,93,243,147,91,186,71,242,48,75,58,1,161,193,151,123,199,251,218,109,211,191,102,239,49,49,64,234,36,172,72,146,128,119,142,90,145,127,121,108,36] -------------------------------------------------------------------------------- /tests/keys/index.ts: -------------------------------------------------------------------------------- 1 | import { Keypair } from '@solana/web3.js'; 2 | import * as fs from 'fs'; 3 | 4 | const DEAL_CONTRACT_KEYS_PATH = process.env.DEAL_CONTRACT_KEYS_PATH; 5 | 6 | export const payerKp: Keypair = (() => { 7 | let secret: Uint8Array = JSON.parse(fs.readFileSync(`${DEAL_CONTRACT_KEYS_PATH}/upgrade_authority.json`, 'utf-8')) 8 | return Keypair.fromSecretKey(new Uint8Array(secret)) 9 | })() 10 | export const mintAuthorityKp: Keypair = (() => { 11 | let secret: Uint8Array = JSON.parse(fs.readFileSync(`${DEAL_CONTRACT_KEYS_PATH}/mint_authority.json`, 'utf-8')) 12 | return Keypair.fromSecretKey(new Uint8Array(secret)) 13 | })() 14 | 15 | export const clientKp: Keypair = (() => { 16 | let secret: Uint8Array = JSON.parse(fs.readFileSync(`${DEAL_CONTRACT_KEYS_PATH}/client.json`, 'utf-8')) 17 | return Keypair.fromSecretKey(new Uint8Array(secret)) 18 | })() 19 | export const executorKp: Keypair = (() => { 20 | let secret: Uint8Array = JSON.parse(fs.readFileSync(`${DEAL_CONTRACT_KEYS_PATH}/executor.json`, 'utf-8')) 21 | return Keypair.fromSecretKey(new Uint8Array(secret)) 22 | })() 23 | export const checkerKp: Keypair = (() => { 24 | let secret: Uint8Array = JSON.parse(fs.readFileSync(`${DEAL_CONTRACT_KEYS_PATH}/checker.json`, 'utf-8')) 25 | return Keypair.fromSecretKey(new Uint8Array(secret)) 26 | })() 27 | export const serviceKp: Keypair = (() => { 28 | let secret: Uint8Array = JSON.parse(fs.readFileSync(`${DEAL_CONTRACT_KEYS_PATH}/service.json`, 'utf-8')) 29 | return Keypair.fromSecretKey(new Uint8Array(secret)) 30 | })() 31 | export const serviceFeeMintKp: Keypair = (() => { 32 | let secret: Uint8Array = JSON.parse(fs.readFileSync(`${DEAL_CONTRACT_KEYS_PATH}/service_fee_mint.json`, 'utf-8')) 33 | return Keypair.fromSecretKey(new Uint8Array(secret)) 34 | })() 35 | export const holderMintKp: Keypair = (() => { 36 | let secret: Uint8Array = JSON.parse(fs.readFileSync(`${DEAL_CONTRACT_KEYS_PATH}/holder_mint.json`, 'utf-8')) 37 | return Keypair.fromSecretKey(new Uint8Array(secret)) 38 | })() 39 | 40 | -------------------------------------------------------------------------------- /tests/keys/mint_authority.json: -------------------------------------------------------------------------------- 1 | [132,213,242,42,193,40,68,132,253,166,173,88,174,209,134,30,216,246,39,74,204,206,186,218,94,129,73,131,140,134,155,1,104,201,159,143,194,61,163,71,84,228,126,79,101,102,83,168,232,12,241,213,91,83,147,174,123,183,8,101,19,248,82,111] -------------------------------------------------------------------------------- /tests/keys/payer.json: -------------------------------------------------------------------------------- 1 | [12,152,127,215,32,44,167,234,129,216,183,179,53,93,108,11,162,74,234,20,13,143,179,254,235,1,5,219,41,0,109,105,18,121,128,139,173,140,143,39,96,85,204,159,55,193,198,184,248,130,235,124,4,254,26,251,106,19,213,240,246,201,8,33] -------------------------------------------------------------------------------- /tests/keys/service.json: -------------------------------------------------------------------------------- 1 | [147,51,32,228,43,121,231,164,49,214,91,163,208,39,100,175,36,204,50,48,185,112,144,3,32,184,40,52,233,245,134,228,165,113,20,118,140,7,225,182,218,29,224,172,180,215,83,156,236,221,152,206,246,73,243,33,50,198,119,129,34,45,41,87] -------------------------------------------------------------------------------- /tests/keys/service_fee_mint.json: -------------------------------------------------------------------------------- 1 | [150,180,23,191,172,10,207,114,234,215,216,211,149,159,108,80,171,22,105,97,59,145,173,185,23,146,77,170,205,48,60,14,74,165,229,30,51,175,28,98,172,29,92,92,196,96,35,176,212,96,244,170,146,129,60,49,163,238,27,149,203,246,150,188] -------------------------------------------------------------------------------- /tests/keys/upgrade_authority.json: -------------------------------------------------------------------------------- 1 | [118,27,171,203,28,176,128,180,135,83,4,121,23,204,30,217,81,183,124,116,161,243,131,82,16,254,98,220,42,172,10,167,28,177,188,140,209,255,206,65,217,199,85,144,81,236,178,196,172,205,192,184,128,73,64,202,169,132,133,121,55,44,124,87] -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "types": ["mocha", "chai"], 4 | "typeRoots": ["./node_modules/@types"], 5 | "lib": ["es2015"], 6 | "module": "commonjs", 7 | "target": "es6", 8 | "esModuleInterop": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.2": 6 | "integrity" "sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==" 7 | "resolved" "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.5.tgz" 8 | "version" "7.22.5" 9 | dependencies: 10 | "regenerator-runtime" "^0.13.11" 11 | 12 | "@coral-xyz/anchor@^0.28.0": 13 | "integrity" "sha512-kQ02Hv2ZqxtWP30WN1d4xxT4QqlOXYDxmEd3k/bbneqhV3X5QMO4LAtoUFs7otxyivOgoqam5Il5qx81FuI4vw==" 14 | "resolved" "https://registry.npmjs.org/@coral-xyz/anchor/-/anchor-0.28.0.tgz" 15 | "version" "0.28.0" 16 | dependencies: 17 | "@coral-xyz/borsh" "^0.28.0" 18 | "@solana/web3.js" "^1.68.0" 19 | "base64-js" "^1.5.1" 20 | "bn.js" "^5.1.2" 21 | "bs58" "^4.0.1" 22 | "buffer-layout" "^1.2.2" 23 | "camelcase" "^6.3.0" 24 | "cross-fetch" "^3.1.5" 25 | "crypto-hash" "^1.3.0" 26 | "eventemitter3" "^4.0.7" 27 | "js-sha256" "^0.9.0" 28 | "pako" "^2.0.3" 29 | "snake-case" "^3.0.4" 30 | "superstruct" "^0.15.4" 31 | "toml" "^3.0.0" 32 | 33 | "@coral-xyz/borsh@^0.28.0": 34 | "integrity" "sha512-/u1VTzw7XooK7rqeD7JLUSwOyRSesPUk0U37BV9zK0axJc1q0nRbKFGFLYCQ16OtdOJTTwGfGp11Lx9B45bRCQ==" 35 | "resolved" "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.28.0.tgz" 36 | "version" "0.28.0" 37 | dependencies: 38 | "bn.js" "^5.1.2" 39 | "buffer-layout" "^1.2.0" 40 | 41 | "@noble/curves@^1.0.0": 42 | "integrity" "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==" 43 | "resolved" "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz" 44 | "version" "1.1.0" 45 | dependencies: 46 | "@noble/hashes" "1.3.1" 47 | 48 | "@noble/hashes@^1.3.0", "@noble/hashes@1.3.1": 49 | "integrity" "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" 50 | "resolved" "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz" 51 | "version" "1.3.1" 52 | 53 | "@solana/buffer-layout-utils@^0.2.0": 54 | "integrity" "sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==" 55 | "resolved" "https://registry.npmjs.org/@solana/buffer-layout-utils/-/buffer-layout-utils-0.2.0.tgz" 56 | "version" "0.2.0" 57 | dependencies: 58 | "@solana/buffer-layout" "^4.0.0" 59 | "@solana/web3.js" "^1.32.0" 60 | "bigint-buffer" "^1.1.5" 61 | "bignumber.js" "^9.0.1" 62 | 63 | "@solana/buffer-layout@^4.0.0": 64 | "integrity" "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==" 65 | "resolved" "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz" 66 | "version" "4.0.1" 67 | dependencies: 68 | "buffer" "~6.0.3" 69 | 70 | "@solana/spl-token@^0.3.5": 71 | "integrity" "sha512-ogwGDcunP9Lkj+9CODOWMiVJEdRtqHAtX2rWF62KxnnSWtMZtV9rDhTrZFshiyJmxDnRL/1nKE1yJHg4jjs3gg==" 72 | "resolved" "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.3.8.tgz" 73 | "version" "0.3.8" 74 | dependencies: 75 | "@solana/buffer-layout" "^4.0.0" 76 | "@solana/buffer-layout-utils" "^0.2.0" 77 | "buffer" "^6.0.3" 78 | 79 | "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.47.4", "@solana/web3.js@^1.68.0": 80 | "integrity" "sha512-PHaO0BdoiQRPpieC1p31wJsBaxwIOWLh8j2ocXNKX8boCQVldt26Jqm2tZE4KlrvnCIV78owPLv1pEUgqhxZ3w==" 81 | "resolved" "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.77.3.tgz" 82 | "version" "1.77.3" 83 | dependencies: 84 | "@babel/runtime" "^7.12.5" 85 | "@noble/curves" "^1.0.0" 86 | "@noble/hashes" "^1.3.0" 87 | "@solana/buffer-layout" "^4.0.0" 88 | "agentkeepalive" "^4.2.1" 89 | "bigint-buffer" "^1.1.5" 90 | "bn.js" "^5.0.0" 91 | "borsh" "^0.7.0" 92 | "bs58" "^4.0.1" 93 | "buffer" "6.0.3" 94 | "fast-stable-stringify" "^1.0.0" 95 | "jayson" "^4.1.0" 96 | "node-fetch" "^2.6.7" 97 | "rpc-websockets" "^7.5.1" 98 | "superstruct" "^0.14.2" 99 | 100 | "@types/bn.js@^5.1.0": 101 | "integrity" "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==" 102 | "resolved" "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz" 103 | "version" "5.1.1" 104 | dependencies: 105 | "@types/node" "*" 106 | 107 | "@types/chai@^4.3.0": 108 | "integrity" "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==" 109 | "resolved" "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz" 110 | "version" "4.3.5" 111 | 112 | "@types/connect@^3.4.33": 113 | "integrity" "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==" 114 | "resolved" "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz" 115 | "version" "3.4.35" 116 | dependencies: 117 | "@types/node" "*" 118 | 119 | "@types/json5@^0.0.29": 120 | "integrity" "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" 121 | "resolved" "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" 122 | "version" "0.0.29" 123 | 124 | "@types/mocha@^9.0.0": 125 | "integrity" "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==" 126 | "resolved" "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz" 127 | "version" "9.1.1" 128 | 129 | "@types/node@*": 130 | "integrity" "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==" 131 | "resolved" "https://registry.npmjs.org/@types/node/-/node-20.3.3.tgz" 132 | "version" "20.3.3" 133 | 134 | "@types/node@^12.12.54": 135 | "integrity" "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" 136 | "resolved" "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz" 137 | "version" "12.20.55" 138 | 139 | "@types/ws@^7.4.4": 140 | "integrity" "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==" 141 | "resolved" "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz" 142 | "version" "7.4.7" 143 | dependencies: 144 | "@types/node" "*" 145 | 146 | "@ungap/promise-all-settled@1.1.2": 147 | "integrity" "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==" 148 | "resolved" "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz" 149 | "version" "1.1.2" 150 | 151 | "agentkeepalive@^4.2.1": 152 | "integrity" "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==" 153 | "resolved" "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz" 154 | "version" "4.3.0" 155 | dependencies: 156 | "debug" "^4.1.0" 157 | "depd" "^2.0.0" 158 | "humanize-ms" "^1.2.1" 159 | 160 | "ansi-colors@4.1.1": 161 | "integrity" "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" 162 | "resolved" "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" 163 | "version" "4.1.1" 164 | 165 | "ansi-regex@^5.0.1": 166 | "integrity" "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" 167 | "resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" 168 | "version" "5.0.1" 169 | 170 | "ansi-styles@^4.0.0", "ansi-styles@^4.1.0": 171 | "integrity" "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==" 172 | "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" 173 | "version" "4.3.0" 174 | dependencies: 175 | "color-convert" "^2.0.1" 176 | 177 | "anymatch@~3.1.2": 178 | "integrity" "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==" 179 | "resolved" "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" 180 | "version" "3.1.3" 181 | dependencies: 182 | "normalize-path" "^3.0.0" 183 | "picomatch" "^2.0.4" 184 | 185 | "argparse@^2.0.1": 186 | "integrity" "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" 187 | "resolved" "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" 188 | "version" "2.0.1" 189 | 190 | "arrify@^1.0.0": 191 | "integrity" "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==" 192 | "resolved" "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz" 193 | "version" "1.0.1" 194 | 195 | "assertion-error@^1.1.0": 196 | "integrity" "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" 197 | "resolved" "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz" 198 | "version" "1.1.0" 199 | 200 | "balanced-match@^1.0.0": 201 | "integrity" "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 202 | "resolved" "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" 203 | "version" "1.0.2" 204 | 205 | "base-x@^3.0.2": 206 | "integrity" "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==" 207 | "resolved" "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz" 208 | "version" "3.0.9" 209 | dependencies: 210 | "safe-buffer" "^5.0.1" 211 | 212 | "base64-js@^1.3.1", "base64-js@^1.5.1": 213 | "integrity" "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 214 | "resolved" "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" 215 | "version" "1.5.1" 216 | 217 | "bigint-buffer@^1.1.5": 218 | "integrity" "sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==" 219 | "resolved" "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz" 220 | "version" "1.1.5" 221 | dependencies: 222 | "bindings" "^1.3.0" 223 | 224 | "bignumber.js@^9.0.1": 225 | "integrity" "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==" 226 | "resolved" "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz" 227 | "version" "9.1.1" 228 | 229 | "binary-extensions@^2.0.0": 230 | "integrity" "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" 231 | "resolved" "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" 232 | "version" "2.2.0" 233 | 234 | "bindings@^1.3.0": 235 | "integrity" "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==" 236 | "resolved" "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz" 237 | "version" "1.5.0" 238 | dependencies: 239 | "file-uri-to-path" "1.0.0" 240 | 241 | "bn.js@^5.0.0", "bn.js@^5.1.2", "bn.js@^5.2.0": 242 | "integrity" "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" 243 | "resolved" "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz" 244 | "version" "5.2.1" 245 | 246 | "borsh@^0.7.0": 247 | "integrity" "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==" 248 | "resolved" "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz" 249 | "version" "0.7.0" 250 | dependencies: 251 | "bn.js" "^5.2.0" 252 | "bs58" "^4.0.0" 253 | "text-encoding-utf-8" "^1.0.2" 254 | 255 | "brace-expansion@^1.1.7": 256 | "integrity" "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==" 257 | "resolved" "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" 258 | "version" "1.1.11" 259 | dependencies: 260 | "balanced-match" "^1.0.0" 261 | "concat-map" "0.0.1" 262 | 263 | "braces@~3.0.2": 264 | "integrity" "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==" 265 | "resolved" "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" 266 | "version" "3.0.2" 267 | dependencies: 268 | "fill-range" "^7.0.1" 269 | 270 | "browser-stdout@1.3.1": 271 | "integrity" "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" 272 | "resolved" "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz" 273 | "version" "1.3.1" 274 | 275 | "bs58@^4.0.0", "bs58@^4.0.1": 276 | "integrity" "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==" 277 | "resolved" "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz" 278 | "version" "4.0.1" 279 | dependencies: 280 | "base-x" "^3.0.2" 281 | 282 | "buffer-from@^1.0.0", "buffer-from@^1.1.0": 283 | "integrity" "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" 284 | "resolved" "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" 285 | "version" "1.1.2" 286 | 287 | "buffer-layout@^1.2.0", "buffer-layout@^1.2.2": 288 | "integrity" "sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==" 289 | "resolved" "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.2.tgz" 290 | "version" "1.2.2" 291 | 292 | "buffer@^6.0.3", "buffer@~6.0.3", "buffer@6.0.3": 293 | "integrity" "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==" 294 | "resolved" "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" 295 | "version" "6.0.3" 296 | dependencies: 297 | "base64-js" "^1.3.1" 298 | "ieee754" "^1.2.1" 299 | 300 | "bufferutil@^4.0.1": 301 | "integrity" "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==" 302 | "resolved" "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz" 303 | "version" "4.0.7" 304 | dependencies: 305 | "node-gyp-build" "^4.3.0" 306 | 307 | "camelcase@^6.0.0", "camelcase@^6.3.0": 308 | "integrity" "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" 309 | "resolved" "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" 310 | "version" "6.3.0" 311 | 312 | "chai@^4.3.4": 313 | "integrity" "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==" 314 | "resolved" "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz" 315 | "version" "4.3.7" 316 | dependencies: 317 | "assertion-error" "^1.1.0" 318 | "check-error" "^1.0.2" 319 | "deep-eql" "^4.1.2" 320 | "get-func-name" "^2.0.0" 321 | "loupe" "^2.3.1" 322 | "pathval" "^1.1.1" 323 | "type-detect" "^4.0.5" 324 | 325 | "chalk@^4.1.0": 326 | "integrity" "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==" 327 | "resolved" "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" 328 | "version" "4.1.2" 329 | dependencies: 330 | "ansi-styles" "^4.1.0" 331 | "supports-color" "^7.1.0" 332 | 333 | "check-error@^1.0.2": 334 | "integrity" "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==" 335 | "resolved" "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz" 336 | "version" "1.0.2" 337 | 338 | "chokidar@3.5.3": 339 | "integrity" "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==" 340 | "resolved" "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" 341 | "version" "3.5.3" 342 | dependencies: 343 | "anymatch" "~3.1.2" 344 | "braces" "~3.0.2" 345 | "glob-parent" "~5.1.2" 346 | "is-binary-path" "~2.1.0" 347 | "is-glob" "~4.0.1" 348 | "normalize-path" "~3.0.0" 349 | "readdirp" "~3.6.0" 350 | optionalDependencies: 351 | "fsevents" "~2.3.2" 352 | 353 | "cliui@^7.0.2": 354 | "integrity" "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==" 355 | "resolved" "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" 356 | "version" "7.0.4" 357 | dependencies: 358 | "string-width" "^4.2.0" 359 | "strip-ansi" "^6.0.0" 360 | "wrap-ansi" "^7.0.0" 361 | 362 | "color-convert@^2.0.1": 363 | "integrity" "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==" 364 | "resolved" "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" 365 | "version" "2.0.1" 366 | dependencies: 367 | "color-name" "~1.1.4" 368 | 369 | "color-name@~1.1.4": 370 | "integrity" "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 371 | "resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" 372 | "version" "1.1.4" 373 | 374 | "commander@^2.20.3": 375 | "integrity" "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" 376 | "resolved" "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" 377 | "version" "2.20.3" 378 | 379 | "concat-map@0.0.1": 380 | "integrity" "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" 381 | "resolved" "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" 382 | "version" "0.0.1" 383 | 384 | "cross-fetch@^3.1.5": 385 | "integrity" "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==" 386 | "resolved" "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz" 387 | "version" "3.1.8" 388 | dependencies: 389 | "node-fetch" "^2.6.12" 390 | 391 | "crypto-hash@^1.3.0": 392 | "integrity" "sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==" 393 | "resolved" "https://registry.npmjs.org/crypto-hash/-/crypto-hash-1.3.0.tgz" 394 | "version" "1.3.0" 395 | 396 | "debug@^4.1.0": 397 | "integrity" "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==" 398 | "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" 399 | "version" "4.3.4" 400 | dependencies: 401 | "ms" "2.1.2" 402 | 403 | "debug@4.3.3": 404 | "integrity" "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==" 405 | "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz" 406 | "version" "4.3.3" 407 | dependencies: 408 | "ms" "2.1.2" 409 | 410 | "decamelize@^4.0.0": 411 | "integrity" "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==" 412 | "resolved" "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz" 413 | "version" "4.0.0" 414 | 415 | "deep-eql@^4.1.2": 416 | "integrity" "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==" 417 | "resolved" "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz" 418 | "version" "4.1.3" 419 | dependencies: 420 | "type-detect" "^4.0.0" 421 | 422 | "delay@^5.0.0": 423 | "integrity" "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==" 424 | "resolved" "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz" 425 | "version" "5.0.0" 426 | 427 | "depd@^2.0.0": 428 | "integrity" "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" 429 | "resolved" "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" 430 | "version" "2.0.0" 431 | 432 | "diff@^3.1.0": 433 | "integrity" "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" 434 | "resolved" "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz" 435 | "version" "3.5.0" 436 | 437 | "diff@5.0.0": 438 | "integrity" "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==" 439 | "resolved" "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz" 440 | "version" "5.0.0" 441 | 442 | "dot-case@^3.0.4": 443 | "integrity" "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==" 444 | "resolved" "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz" 445 | "version" "3.0.4" 446 | dependencies: 447 | "no-case" "^3.0.4" 448 | "tslib" "^2.0.3" 449 | 450 | "emoji-regex@^8.0.0": 451 | "integrity" "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 452 | "resolved" "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" 453 | "version" "8.0.0" 454 | 455 | "es6-promise@^4.0.3": 456 | "integrity" "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" 457 | "resolved" "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz" 458 | "version" "4.2.8" 459 | 460 | "es6-promisify@^5.0.0": 461 | "integrity" "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==" 462 | "resolved" "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz" 463 | "version" "5.0.0" 464 | dependencies: 465 | "es6-promise" "^4.0.3" 466 | 467 | "escalade@^3.1.1": 468 | "integrity" "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" 469 | "resolved" "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" 470 | "version" "3.1.1" 471 | 472 | "escape-string-regexp@4.0.0": 473 | "integrity" "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" 474 | "resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" 475 | "version" "4.0.0" 476 | 477 | "eventemitter3@^4.0.7": 478 | "integrity" "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" 479 | "resolved" "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" 480 | "version" "4.0.7" 481 | 482 | "eyes@^0.1.8": 483 | "integrity" "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==" 484 | "resolved" "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz" 485 | "version" "0.1.8" 486 | 487 | "fast-stable-stringify@^1.0.0": 488 | "integrity" "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==" 489 | "resolved" "https://registry.npmjs.org/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz" 490 | "version" "1.0.0" 491 | 492 | "file-uri-to-path@1.0.0": 493 | "integrity" "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" 494 | "resolved" "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz" 495 | "version" "1.0.0" 496 | 497 | "fill-range@^7.0.1": 498 | "integrity" "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==" 499 | "resolved" "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" 500 | "version" "7.0.1" 501 | dependencies: 502 | "to-regex-range" "^5.0.1" 503 | 504 | "find-up@5.0.0": 505 | "integrity" "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==" 506 | "resolved" "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" 507 | "version" "5.0.0" 508 | dependencies: 509 | "locate-path" "^6.0.0" 510 | "path-exists" "^4.0.0" 511 | 512 | "flat@^5.0.2": 513 | "integrity" "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==" 514 | "resolved" "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" 515 | "version" "5.0.2" 516 | 517 | "fs.realpath@^1.0.0": 518 | "integrity" "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" 519 | "resolved" "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" 520 | "version" "1.0.0" 521 | 522 | "get-caller-file@^2.0.5": 523 | "integrity" "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" 524 | "resolved" "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" 525 | "version" "2.0.5" 526 | 527 | "get-func-name@^2.0.0": 528 | "integrity" "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==" 529 | "resolved" "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz" 530 | "version" "2.0.0" 531 | 532 | "glob-parent@~5.1.2": 533 | "integrity" "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==" 534 | "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" 535 | "version" "5.1.2" 536 | dependencies: 537 | "is-glob" "^4.0.1" 538 | 539 | "glob@7.2.0": 540 | "integrity" "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==" 541 | "resolved" "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" 542 | "version" "7.2.0" 543 | dependencies: 544 | "fs.realpath" "^1.0.0" 545 | "inflight" "^1.0.4" 546 | "inherits" "2" 547 | "minimatch" "^3.0.4" 548 | "once" "^1.3.0" 549 | "path-is-absolute" "^1.0.0" 550 | 551 | "growl@1.10.5": 552 | "integrity" "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" 553 | "resolved" "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz" 554 | "version" "1.10.5" 555 | 556 | "has-flag@^4.0.0": 557 | "integrity" "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" 558 | "resolved" "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" 559 | "version" "4.0.0" 560 | 561 | "he@1.2.0": 562 | "integrity" "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" 563 | "resolved" "https://registry.npmjs.org/he/-/he-1.2.0.tgz" 564 | "version" "1.2.0" 565 | 566 | "humanize-ms@^1.2.1": 567 | "integrity" "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==" 568 | "resolved" "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz" 569 | "version" "1.2.1" 570 | dependencies: 571 | "ms" "^2.0.0" 572 | 573 | "ieee754@^1.2.1": 574 | "integrity" "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" 575 | "resolved" "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" 576 | "version" "1.2.1" 577 | 578 | "inflight@^1.0.4": 579 | "integrity" "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==" 580 | "resolved" "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" 581 | "version" "1.0.6" 582 | dependencies: 583 | "once" "^1.3.0" 584 | "wrappy" "1" 585 | 586 | "inherits@2": 587 | "integrity" "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 588 | "resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" 589 | "version" "2.0.4" 590 | 591 | "is-binary-path@~2.1.0": 592 | "integrity" "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==" 593 | "resolved" "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" 594 | "version" "2.1.0" 595 | dependencies: 596 | "binary-extensions" "^2.0.0" 597 | 598 | "is-extglob@^2.1.1": 599 | "integrity" "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" 600 | "resolved" "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" 601 | "version" "2.1.1" 602 | 603 | "is-fullwidth-code-point@^3.0.0": 604 | "integrity" "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" 605 | "resolved" "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" 606 | "version" "3.0.0" 607 | 608 | "is-glob@^4.0.1", "is-glob@~4.0.1": 609 | "integrity" "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==" 610 | "resolved" "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" 611 | "version" "4.0.3" 612 | dependencies: 613 | "is-extglob" "^2.1.1" 614 | 615 | "is-number@^7.0.0": 616 | "integrity" "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" 617 | "resolved" "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" 618 | "version" "7.0.0" 619 | 620 | "is-plain-obj@^2.1.0": 621 | "integrity" "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" 622 | "resolved" "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" 623 | "version" "2.1.0" 624 | 625 | "is-unicode-supported@^0.1.0": 626 | "integrity" "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==" 627 | "resolved" "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" 628 | "version" "0.1.0" 629 | 630 | "isexe@^2.0.0": 631 | "integrity" "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" 632 | "resolved" "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" 633 | "version" "2.0.0" 634 | 635 | "isomorphic-ws@^4.0.1": 636 | "integrity" "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==" 637 | "resolved" "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz" 638 | "version" "4.0.1" 639 | 640 | "jayson@^4.1.0": 641 | "integrity" "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==" 642 | "resolved" "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz" 643 | "version" "4.1.0" 644 | dependencies: 645 | "@types/connect" "^3.4.33" 646 | "@types/node" "^12.12.54" 647 | "@types/ws" "^7.4.4" 648 | "commander" "^2.20.3" 649 | "delay" "^5.0.0" 650 | "es6-promisify" "^5.0.0" 651 | "eyes" "^0.1.8" 652 | "isomorphic-ws" "^4.0.1" 653 | "json-stringify-safe" "^5.0.1" 654 | "JSONStream" "^1.3.5" 655 | "uuid" "^8.3.2" 656 | "ws" "^7.4.5" 657 | 658 | "js-sha256@^0.9.0": 659 | "integrity" "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" 660 | "resolved" "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz" 661 | "version" "0.9.0" 662 | 663 | "js-yaml@4.1.0": 664 | "integrity" "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==" 665 | "resolved" "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" 666 | "version" "4.1.0" 667 | dependencies: 668 | "argparse" "^2.0.1" 669 | 670 | "json-stringify-safe@^5.0.1": 671 | "integrity" "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" 672 | "resolved" "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" 673 | "version" "5.0.1" 674 | 675 | "json5@^1.0.2": 676 | "integrity" "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==" 677 | "resolved" "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz" 678 | "version" "1.0.2" 679 | dependencies: 680 | "minimist" "^1.2.0" 681 | 682 | "jsonparse@^1.2.0": 683 | "integrity" "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==" 684 | "resolved" "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz" 685 | "version" "1.3.1" 686 | 687 | "JSONStream@^1.3.5": 688 | "integrity" "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==" 689 | "resolved" "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" 690 | "version" "1.3.5" 691 | dependencies: 692 | "jsonparse" "^1.2.0" 693 | "through" ">=2.2.7 <3" 694 | 695 | "locate-path@^6.0.0": 696 | "integrity" "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==" 697 | "resolved" "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" 698 | "version" "6.0.0" 699 | dependencies: 700 | "p-locate" "^5.0.0" 701 | 702 | "log-symbols@4.1.0": 703 | "integrity" "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==" 704 | "resolved" "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" 705 | "version" "4.1.0" 706 | dependencies: 707 | "chalk" "^4.1.0" 708 | "is-unicode-supported" "^0.1.0" 709 | 710 | "loupe@^2.3.1": 711 | "integrity" "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==" 712 | "resolved" "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz" 713 | "version" "2.3.6" 714 | dependencies: 715 | "get-func-name" "^2.0.0" 716 | 717 | "lower-case@^2.0.2": 718 | "integrity" "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==" 719 | "resolved" "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz" 720 | "version" "2.0.2" 721 | dependencies: 722 | "tslib" "^2.0.3" 723 | 724 | "make-error@^1.1.1": 725 | "integrity" "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" 726 | "resolved" "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" 727 | "version" "1.3.6" 728 | 729 | "minimatch@^3.0.4": 730 | "integrity" "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==" 731 | "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" 732 | "version" "3.1.2" 733 | dependencies: 734 | "brace-expansion" "^1.1.7" 735 | 736 | "minimatch@4.2.1": 737 | "integrity" "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==" 738 | "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz" 739 | "version" "4.2.1" 740 | dependencies: 741 | "brace-expansion" "^1.1.7" 742 | 743 | "minimist@^1.2.0", "minimist@^1.2.6": 744 | "integrity" "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" 745 | "resolved" "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" 746 | "version" "1.2.8" 747 | 748 | "mkdirp@^0.5.1": 749 | "integrity" "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==" 750 | "resolved" "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" 751 | "version" "0.5.6" 752 | dependencies: 753 | "minimist" "^1.2.6" 754 | 755 | "mocha@^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X", "mocha@^9.0.3": 756 | "integrity" "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==" 757 | "resolved" "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz" 758 | "version" "9.2.2" 759 | dependencies: 760 | "@ungap/promise-all-settled" "1.1.2" 761 | "ansi-colors" "4.1.1" 762 | "browser-stdout" "1.3.1" 763 | "chokidar" "3.5.3" 764 | "debug" "4.3.3" 765 | "diff" "5.0.0" 766 | "escape-string-regexp" "4.0.0" 767 | "find-up" "5.0.0" 768 | "glob" "7.2.0" 769 | "growl" "1.10.5" 770 | "he" "1.2.0" 771 | "js-yaml" "4.1.0" 772 | "log-symbols" "4.1.0" 773 | "minimatch" "4.2.1" 774 | "ms" "2.1.3" 775 | "nanoid" "3.3.1" 776 | "serialize-javascript" "6.0.0" 777 | "strip-json-comments" "3.1.1" 778 | "supports-color" "8.1.1" 779 | "which" "2.0.2" 780 | "workerpool" "6.2.0" 781 | "yargs" "16.2.0" 782 | "yargs-parser" "20.2.4" 783 | "yargs-unparser" "2.0.0" 784 | 785 | "ms@^2.0.0", "ms@2.1.2": 786 | "integrity" "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 787 | "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" 788 | "version" "2.1.2" 789 | 790 | "ms@2.1.3": 791 | "integrity" "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 792 | "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" 793 | "version" "2.1.3" 794 | 795 | "nanoid@3.3.1": 796 | "integrity" "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==" 797 | "resolved" "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz" 798 | "version" "3.3.1" 799 | 800 | "no-case@^3.0.4": 801 | "integrity" "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==" 802 | "resolved" "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz" 803 | "version" "3.0.4" 804 | dependencies: 805 | "lower-case" "^2.0.2" 806 | "tslib" "^2.0.3" 807 | 808 | "node-fetch@^2.6.12", "node-fetch@^2.6.7": 809 | "integrity" "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==" 810 | "resolved" "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz" 811 | "version" "2.6.12" 812 | dependencies: 813 | "whatwg-url" "^5.0.0" 814 | 815 | "node-gyp-build@^4.3.0": 816 | "integrity" "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==" 817 | "resolved" "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz" 818 | "version" "4.6.0" 819 | 820 | "normalize-path@^3.0.0", "normalize-path@~3.0.0": 821 | "integrity" "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" 822 | "resolved" "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" 823 | "version" "3.0.0" 824 | 825 | "once@^1.3.0": 826 | "integrity" "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==" 827 | "resolved" "https://registry.npmjs.org/once/-/once-1.4.0.tgz" 828 | "version" "1.4.0" 829 | dependencies: 830 | "wrappy" "1" 831 | 832 | "p-limit@^3.0.2": 833 | "integrity" "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==" 834 | "resolved" "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" 835 | "version" "3.1.0" 836 | dependencies: 837 | "yocto-queue" "^0.1.0" 838 | 839 | "p-locate@^5.0.0": 840 | "integrity" "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==" 841 | "resolved" "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" 842 | "version" "5.0.0" 843 | dependencies: 844 | "p-limit" "^3.0.2" 845 | 846 | "pako@^2.0.3": 847 | "integrity" "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" 848 | "resolved" "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz" 849 | "version" "2.1.0" 850 | 851 | "path-exists@^4.0.0": 852 | "integrity" "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" 853 | "resolved" "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" 854 | "version" "4.0.0" 855 | 856 | "path-is-absolute@^1.0.0": 857 | "integrity" "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" 858 | "resolved" "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" 859 | "version" "1.0.1" 860 | 861 | "pathval@^1.1.1": 862 | "integrity" "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==" 863 | "resolved" "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz" 864 | "version" "1.1.1" 865 | 866 | "picomatch@^2.0.4", "picomatch@^2.2.1": 867 | "integrity" "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" 868 | "resolved" "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" 869 | "version" "2.3.1" 870 | 871 | "prettier@^2.6.2": 872 | "integrity" "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==" 873 | "resolved" "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz" 874 | "version" "2.8.8" 875 | 876 | "randombytes@^2.1.0": 877 | "integrity" "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==" 878 | "resolved" "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" 879 | "version" "2.1.0" 880 | dependencies: 881 | "safe-buffer" "^5.1.0" 882 | 883 | "readdirp@~3.6.0": 884 | "integrity" "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==" 885 | "resolved" "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" 886 | "version" "3.6.0" 887 | dependencies: 888 | "picomatch" "^2.2.1" 889 | 890 | "regenerator-runtime@^0.13.11": 891 | "integrity" "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" 892 | "resolved" "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz" 893 | "version" "0.13.11" 894 | 895 | "require-directory@^2.1.1": 896 | "integrity" "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" 897 | "resolved" "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" 898 | "version" "2.1.1" 899 | 900 | "rpc-websockets@^7.5.1": 901 | "integrity" "sha512-kGFkeTsmd37pHPMaHIgN1LVKXMi0JD782v4Ds9ZKtLlwdTKjn+CxM9A9/gLT2LaOuEcEFGL98h1QWQtlOIdW0w==" 902 | "resolved" "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-7.5.1.tgz" 903 | "version" "7.5.1" 904 | dependencies: 905 | "@babel/runtime" "^7.17.2" 906 | "eventemitter3" "^4.0.7" 907 | "uuid" "^8.3.2" 908 | "ws" "^8.5.0" 909 | optionalDependencies: 910 | "bufferutil" "^4.0.1" 911 | "utf-8-validate" "^5.0.2" 912 | 913 | "safe-buffer@^5.0.1", "safe-buffer@^5.1.0": 914 | "integrity" "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 915 | "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" 916 | "version" "5.2.1" 917 | 918 | "serialize-javascript@6.0.0": 919 | "integrity" "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==" 920 | "resolved" "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz" 921 | "version" "6.0.0" 922 | dependencies: 923 | "randombytes" "^2.1.0" 924 | 925 | "snake-case@^3.0.4": 926 | "integrity" "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==" 927 | "resolved" "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz" 928 | "version" "3.0.4" 929 | dependencies: 930 | "dot-case" "^3.0.4" 931 | "tslib" "^2.0.3" 932 | 933 | "source-map-support@^0.5.6": 934 | "integrity" "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==" 935 | "resolved" "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" 936 | "version" "0.5.21" 937 | dependencies: 938 | "buffer-from" "^1.0.0" 939 | "source-map" "^0.6.0" 940 | 941 | "source-map@^0.6.0": 942 | "integrity" "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" 943 | "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" 944 | "version" "0.6.1" 945 | 946 | "string-width@^4.1.0", "string-width@^4.2.0": 947 | "integrity" "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==" 948 | "resolved" "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" 949 | "version" "4.2.3" 950 | dependencies: 951 | "emoji-regex" "^8.0.0" 952 | "is-fullwidth-code-point" "^3.0.0" 953 | "strip-ansi" "^6.0.1" 954 | 955 | "strip-ansi@^6.0.0", "strip-ansi@^6.0.1": 956 | "integrity" "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==" 957 | "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" 958 | "version" "6.0.1" 959 | dependencies: 960 | "ansi-regex" "^5.0.1" 961 | 962 | "strip-bom@^3.0.0": 963 | "integrity" "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" 964 | "resolved" "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" 965 | "version" "3.0.0" 966 | 967 | "strip-json-comments@3.1.1": 968 | "integrity" "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" 969 | "resolved" "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" 970 | "version" "3.1.1" 971 | 972 | "superstruct@^0.14.2": 973 | "integrity" "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==" 974 | "resolved" "https://registry.npmjs.org/superstruct/-/superstruct-0.14.2.tgz" 975 | "version" "0.14.2" 976 | 977 | "superstruct@^0.15.4": 978 | "integrity" "sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==" 979 | "resolved" "https://registry.npmjs.org/superstruct/-/superstruct-0.15.5.tgz" 980 | "version" "0.15.5" 981 | 982 | "supports-color@^7.1.0": 983 | "integrity" "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==" 984 | "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" 985 | "version" "7.2.0" 986 | dependencies: 987 | "has-flag" "^4.0.0" 988 | 989 | "supports-color@8.1.1": 990 | "integrity" "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==" 991 | "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" 992 | "version" "8.1.1" 993 | dependencies: 994 | "has-flag" "^4.0.0" 995 | 996 | "text-encoding-utf-8@^1.0.2": 997 | "integrity" "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==" 998 | "resolved" "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz" 999 | "version" "1.0.2" 1000 | 1001 | "through@>=2.2.7 <3": 1002 | "integrity" "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" 1003 | "resolved" "https://registry.npmjs.org/through/-/through-2.3.8.tgz" 1004 | "version" "2.3.8" 1005 | 1006 | "to-regex-range@^5.0.1": 1007 | "integrity" "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==" 1008 | "resolved" "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" 1009 | "version" "5.0.1" 1010 | dependencies: 1011 | "is-number" "^7.0.0" 1012 | 1013 | "toml@^3.0.0": 1014 | "integrity" "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==" 1015 | "resolved" "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz" 1016 | "version" "3.0.0" 1017 | 1018 | "tr46@~0.0.3": 1019 | "integrity" "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 1020 | "resolved" "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" 1021 | "version" "0.0.3" 1022 | 1023 | "ts-mocha@^10.0.0": 1024 | "integrity" "sha512-VRfgDO+iiuJFlNB18tzOfypJ21xn2xbuZyDvJvqpTbWgkAgD17ONGr8t+Tl8rcBtOBdjXp5e/Rk+d39f7XBHRw==" 1025 | "resolved" "https://registry.npmjs.org/ts-mocha/-/ts-mocha-10.0.0.tgz" 1026 | "version" "10.0.0" 1027 | dependencies: 1028 | "ts-node" "7.0.1" 1029 | optionalDependencies: 1030 | "tsconfig-paths" "^3.5.0" 1031 | 1032 | "ts-node@7.0.1": 1033 | "integrity" "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==" 1034 | "resolved" "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz" 1035 | "version" "7.0.1" 1036 | dependencies: 1037 | "arrify" "^1.0.0" 1038 | "buffer-from" "^1.1.0" 1039 | "diff" "^3.1.0" 1040 | "make-error" "^1.1.1" 1041 | "minimist" "^1.2.0" 1042 | "mkdirp" "^0.5.1" 1043 | "source-map-support" "^0.5.6" 1044 | "yn" "^2.0.0" 1045 | 1046 | "tsconfig-paths@^3.5.0": 1047 | "integrity" "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==" 1048 | "resolved" "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz" 1049 | "version" "3.14.2" 1050 | dependencies: 1051 | "@types/json5" "^0.0.29" 1052 | "json5" "^1.0.2" 1053 | "minimist" "^1.2.6" 1054 | "strip-bom" "^3.0.0" 1055 | 1056 | "tslib@^2.0.3": 1057 | "integrity" "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" 1058 | "resolved" "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz" 1059 | "version" "2.6.0" 1060 | 1061 | "type-detect@^4.0.0", "type-detect@^4.0.5": 1062 | "integrity" "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" 1063 | "resolved" "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" 1064 | "version" "4.0.8" 1065 | 1066 | "typescript@^4.3.5": 1067 | "integrity" "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" 1068 | "resolved" "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" 1069 | "version" "4.9.5" 1070 | 1071 | "utf-8-validate@^5.0.2", "utf-8-validate@>=5.0.2": 1072 | "integrity" "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==" 1073 | "resolved" "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz" 1074 | "version" "5.0.10" 1075 | dependencies: 1076 | "node-gyp-build" "^4.3.0" 1077 | 1078 | "uuid@^8.3.2": 1079 | "integrity" "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" 1080 | "resolved" "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" 1081 | "version" "8.3.2" 1082 | 1083 | "uuid@^9.0.0": 1084 | "integrity" "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" 1085 | "resolved" "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz" 1086 | "version" "9.0.0" 1087 | 1088 | "webidl-conversions@^3.0.0": 1089 | "integrity" "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 1090 | "resolved" "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" 1091 | "version" "3.0.1" 1092 | 1093 | "whatwg-url@^5.0.0": 1094 | "integrity" "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==" 1095 | "resolved" "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" 1096 | "version" "5.0.0" 1097 | dependencies: 1098 | "tr46" "~0.0.3" 1099 | "webidl-conversions" "^3.0.0" 1100 | 1101 | "which@2.0.2": 1102 | "integrity" "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==" 1103 | "resolved" "https://registry.npmjs.org/which/-/which-2.0.2.tgz" 1104 | "version" "2.0.2" 1105 | dependencies: 1106 | "isexe" "^2.0.0" 1107 | 1108 | "workerpool@6.2.0": 1109 | "integrity" "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==" 1110 | "resolved" "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz" 1111 | "version" "6.2.0" 1112 | 1113 | "wrap-ansi@^7.0.0": 1114 | "integrity" "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==" 1115 | "resolved" "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" 1116 | "version" "7.0.0" 1117 | dependencies: 1118 | "ansi-styles" "^4.0.0" 1119 | "string-width" "^4.1.0" 1120 | "strip-ansi" "^6.0.0" 1121 | 1122 | "wrappy@1": 1123 | "integrity" "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 1124 | "resolved" "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" 1125 | "version" "1.0.2" 1126 | 1127 | "ws@*", "ws@^7.4.5": 1128 | "integrity" "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==" 1129 | "resolved" "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz" 1130 | "version" "7.5.9" 1131 | 1132 | "ws@^8.5.0": 1133 | "integrity" "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==" 1134 | "resolved" "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz" 1135 | "version" "8.13.0" 1136 | 1137 | "y18n@^5.0.5": 1138 | "integrity" "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" 1139 | "resolved" "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" 1140 | "version" "5.0.8" 1141 | 1142 | "yargs-parser@^20.2.2", "yargs-parser@20.2.4": 1143 | "integrity" "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==" 1144 | "resolved" "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz" 1145 | "version" "20.2.4" 1146 | 1147 | "yargs-unparser@2.0.0": 1148 | "integrity" "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==" 1149 | "resolved" "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz" 1150 | "version" "2.0.0" 1151 | dependencies: 1152 | "camelcase" "^6.0.0" 1153 | "decamelize" "^4.0.0" 1154 | "flat" "^5.0.2" 1155 | "is-plain-obj" "^2.1.0" 1156 | 1157 | "yargs@16.2.0": 1158 | "integrity" "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==" 1159 | "resolved" "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" 1160 | "version" "16.2.0" 1161 | dependencies: 1162 | "cliui" "^7.0.2" 1163 | "escalade" "^3.1.1" 1164 | "get-caller-file" "^2.0.5" 1165 | "require-directory" "^2.1.1" 1166 | "string-width" "^4.2.0" 1167 | "y18n" "^5.0.5" 1168 | "yargs-parser" "^20.2.2" 1169 | 1170 | "yn@^2.0.0": 1171 | "integrity" "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==" 1172 | "resolved" "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz" 1173 | "version" "2.0.0" 1174 | 1175 | "yocto-queue@^0.1.0": 1176 | "integrity" "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" 1177 | "resolved" "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" 1178 | "version" "0.1.0" 1179 | --------------------------------------------------------------------------------