diff --git a/.gitignore b/.gitignore index 355cc3a8..6fad0c67 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,4 @@ src/api/marketplace/dump.rdb # solana-ts solana-ts-node/node_modules/* + diff --git a/solana-client/components/CancelEscrowSpl.tsx b/solana-client/components/CancelEscrowSpl.tsx index dec4f123..e9817dea 100644 --- a/solana-client/components/CancelEscrowSpl.tsx +++ b/solana-client/components/CancelEscrowSpl.tsx @@ -46,17 +46,19 @@ interface CancelEscrowSplProps { business: string, validatorAuthority: string, mintTokenAccount: string, - orderCode: number + orderCode: number, + network: string } export const CancelEscrowSpl: FC<CancelEscrowSplProps> = ( {business, validatorAuthority, mintTokenAccount, - orderCode}) => { + orderCode, + network}) => { const wallet = useAnchorWallet() - const connection = new Connection(clusterApiUrl('devnet'), + const connection = new Connection(network, { commitment: "confirmed", confirmTransactionInitialTimeout: 30000 diff --git a/solana-client/components/CreateEscrowSolana.tsx b/solana-client/components/CreateEscrowSolana.tsx index ee7e0824..9e8a4bbf 100644 --- a/solana-client/components/CreateEscrowSolana.tsx +++ b/solana-client/components/CreateEscrowSolana.tsx @@ -40,6 +40,8 @@ export const CreateEscrowSolana: FC<CreateEscrowSolanaProps> = ({validator, busi commitment: "confirmed", confirmTransactionInitialTimeout: 30000 }); + + const program = utils.getAnchorProgram(connection); const provider = new AnchorProvider(connection, wallet, {}) setProvider(provider) diff --git a/solana-client/components/Validate.tsx b/solana-client/components/Validate.tsx index 1b4bc0eb..06daf320 100644 --- a/solana-client/components/Validate.tsx +++ b/solana-client/components/Validate.tsx @@ -53,7 +53,9 @@ export const Validate: FC<ValidateProps> = ({validator, const program = utils.getAnchorProgram(connection); const provider = new AnchorProvider(connection, wallet, {}) + setProvider(provider) + const { publicKey, signTransaction, sendTransaction } = useWallet() if (!connection || !publicKey) { diff --git a/solana-client/pages/index.tsx b/solana-client/pages/index.tsx index 6814e0db..ce256c63 100644 --- a/solana-client/pages/index.tsx +++ b/solana-client/pages/index.tsx @@ -225,7 +225,8 @@ const Home: NextPage = (props) => { <CancelEscrowSpl business={BUSINESS} validatorAuthority={VALIDATOR} mintTokenAccount={MINT} - orderCode={ORDER_CODE} /> + orderCode={ORDER_CODE} + network={NETWORK} /> <ValidateEscrowSpl validator={VALIDATOR} diff --git a/solana-python/anchorpy_dynamic.py b/solana-python/anchorpy_dynamic.py new file mode 100644 index 00000000..6c6f5e28 --- /dev/null +++ b/solana-python/anchorpy_dynamic.py @@ -0,0 +1,22 @@ +from pathlib import Path +import asyncio +import json +from solders.pubkey import Pubkey +from anchorpy import Idl, Program + +async def main(): + # Read the generated IDL. + with Path("xfluencer.json").open() as f: + raw_idl = f.read() + idl = Idl.from_json(raw_idl) + # Address of the deployed program. + program_id = Pubkey.from_string(idl.metadata['address']) + # Generate the program client from IDL. + async with Program(idl, program_id) as program: + # Execute the RPC. + await program.rpc["initialize"]() + # If we don't use the context manager, we need to + # close the underlying http client, otherwise we get warnings. + await program.close() + +asyncio.run(main()) \ No newline at end of file diff --git a/solana-python/config.json b/solana-python/config.json index bdd69f3a..c515be96 100644 --- a/solana-python/config.json +++ b/solana-python/config.json @@ -1,5 +1,6 @@ { - "network": "https://devnet.helius-rpc.com/?api-key=b57191c8-c14e-4ae2-83b6-1ab88c2f3605", + "network": "https://mainnet.helius-rpc.com/?api-key=b57191c8-c14e-4ae2-83b6-1ab88c2f3605", + "network-dev":"https://devnet.helius-rpc.com/?api-key=b57191c8-c14e-4ae2-83b6-1ab88c2f3605", "rpc":{ "mainnet":"https://bold-hidden-glade.solana-mainnet.quiknode.pro/bcd715dccef5e699ea43459b691a09c2bc8dc474", "devnet":"" @@ -10,7 +11,7 @@ "usdc_ata_localnet": "8TkaDh6WGExnXpvBeoCPwY9XNNd7dK3Nn5W3y1Q2djJV" }, "business": { - "pubkey":"GQRDv58u1dULSyHSYWPqNTjcWjsFHHi763mbqDaEEgQ3", + "pubkey":"6suvWCcjg5o7xgHrDGc4MXQxraK9PnZyEXzjhhQN6HUK", "usdc_ata": "B8LnJbxHoDzcA7GCTz3C9H819sqVe8Ro51WMJDGLZHCe", "usdc_ata_localnet": "75toiYLG8AiEktDnYMwToHZ3RJrkpiSyapWknv9yTVs9" }, @@ -28,8 +29,8 @@ }, "platform": "EsYxpj9ADJyGEjMv3tyDpADv33jDPkv9uLymXWwQCiwH", "amount":{ - "lamports":10000000, - "usdc": 1000000 + "lamports":10000, + "usdc": 1 }, "order_code": 36831, "program_id":{ @@ -40,3 +41,4 @@ "mainnet": "DmYaabL1PhacWWsRwyZpBqBP9n7tVq7115zG2tznYLb9" } } +g \ No newline at end of file diff --git a/solana-python/config.py b/solana-python/config.py index cae781df..7c0e93b9 100644 --- a/solana-python/config.py +++ b/solana-python/config.py @@ -10,7 +10,7 @@ def load_configuration(config_file="config.json"): configuration = load_configuration() -program_id = configuration["program_id"]["devnet"] +program_id = configuration["program_id"]["mainnet"] print("Program Id Selected From Configuration:", program_id) PROGRAM_ID = Pubkey.from_string(program_id) @@ -18,15 +18,22 @@ def load_configuration(config_file="config.json"): @dataclass class KeypairPaths: home: str = os.getenv("HOME") + "/influencer-marketplace/solana-python/test_wallets" + platform: str = "platform_EsYxpj9ADJyGEjMv3tyDpADv33jDPkv9uLymXWwQCiwH.json" validation_authority: str = f"{home}/{platform}" + business: str = "business_6suvWCcjg5o7xgHrDGc4MXQxraK9PnZyEXzjhhQN6HUK.json" bussines_keypair: str = f"{home}/{business}" + influencer: str = "influencer_94fznXq73oweXLrg2zL75XAMy9xNEbqtb191Xcrq97QA.json" influencer_keypair: str = f"{home}/{influencer}" + + + + @dataclass class SplTokens: usdc: str = "Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr" - usdc_alt: str = "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU" \ No newline at end of file + usdc_alt: str = "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU" diff --git a/solana-python/launch_cancel_escrow.py b/solana-python/launch_cancel_escrow.py index d4e2bd72..500c9946 100644 --- a/solana-python/launch_cancel_escrow.py +++ b/solana-python/launch_cancel_escrow.py @@ -11,12 +11,15 @@ from pyxfluencer.instructions import cancel_escrow_sol -async def main(): - - msg = "Cancel Escrow Amount by Business" +def print_tittle(msg): print(len(msg)*"*") print(msg) print(len(msg)*"*") + +async def main(): + + msg = "Cancel Escrow Amount by Business" + print_tittle(msg) configuration = load_configuration() network = configuration["network"] @@ -26,10 +29,12 @@ async def main(): keypair_paths = KeypairPaths() business, business_pk = get_local_keypair_pubkey(path=keypair_paths.bussines_keypair) + #business, business_pk = get_local_keypair_pubkey() + _, influencer_pk = get_local_keypair_pubkey(path=keypair_paths.influencer_keypair) - assert str(business_pk) == configuration["business"] - assert str(influencer_pk) == configuration["influencer"] + assert str(business_pk) == configuration["business"]["pubkey"] + assert str(influencer_pk) == configuration["influencer"]["pubkey"] order_code = configuration["order_code"] @@ -48,8 +53,8 @@ async def main(): "escrow_account":escrow_pda} ix = cancel_escrow_sol(accounts) - opts = TxOpts(skip_confirmation = True, - skip_preflight = True) + opts = TxOpts(skip_confirmation = False, + skip_preflight = False) signers = [business] diff --git a/solana-python/launch_claim_escrow.py b/solana-python/launch_claim_escrow.py index 917b53b0..b27c521f 100644 --- a/solana-python/launch_claim_escrow.py +++ b/solana-python/launch_claim_escrow.py @@ -18,20 +18,20 @@ async def main(): # implement caller to claim escrow instruction configuration = load_configuration() - network = configuration["network"] + network = configuration["rpc"]["mainnet"] print(f"Network: {network} Program ID: {PROGRAM_ID}") keypair_paths = KeypairPaths() # do not need keypair from business just the public key - _, business_pk = get_local_keypair_pubkey(path=keypair_paths.bussines_keypair) + _, business_pk = get_local_keypair_pubkey() #path=keypair_paths.bussines_keypair) # keypair needed here is the one from influcner because it sings the transaction influencer, influencer_pk = get_local_keypair_pubkey(path=keypair_paths.influencer_keypair) - assert str(business_pk) == configuration["business"] - assert str(influencer_pk) == configuration["influencer"] + assert str(business_pk) == configuration["business"]["pubkey"] + assert str(influencer_pk) == configuration["influencer"]["pubkey"] order_code = configuration["order_code"] @@ -51,7 +51,7 @@ async def main(): ix = claim_escrow(args, accounts, program_id=PROGRAM_ID) opts = TxOpts(skip_confirmation = True, - skip_preflight = True) + skip_preflight = False) signers = [influencer] diff --git a/solana-python/launch_create_escrow.py b/solana-python/launch_create_escrow.py index 0c5d6a2e..75086402 100644 --- a/solana-python/launch_create_escrow.py +++ b/solana-python/launch_create_escrow.py @@ -1,7 +1,14 @@ -from solders.pubkey import Pubkey from solana.rpc.types import TxOpts -from pyxfluencer.utils import get_local_keypair_pubkey +from pathlib import Path +from solders.pubkey import Pubkey # type: ignore +from anchorpy import Idl, Program + +import asyncio +from solders.system_program import ID as SYS_PROGRAM_ID + + +from pyxfluencer.utils import get_local_keypair_pubkey, select_client from pyxfluencer.utils import sign_and_send_transaction from pyxfluencer.program_id import PROGRAM_ID @@ -9,19 +16,24 @@ from config import KeypairPaths, load_configuration -async def main(): - - - msg = "Create Escrow for Business and Influencer with an amount and an order code" +def print_tittle(msg): print(len(msg)*"*") print(msg) print(len(msg)*"*") + +async def main(): + + msg = "Create Escrow for Business and Influencer with an amount and an order code" + print_tittle(msg) configuration = load_configuration() network = configuration["rpc"]["mainnet"] - print(f"Network: {network}") + print("Network selected: ",network) + client = select_client(network=network, async_client=False) + + print(f"Program ID: {PROGRAM_ID}") keypair_paths = KeypairPaths() @@ -38,11 +50,28 @@ async def main(): assert str(business_pk) == configuration["business"]["pubkey"] assert str(influencer_pk) == configuration["influencer"]["pubkey"] + ### get account info on tx accounts before sending it + accounts = [validation_authority_pk, + business_pk, + influencer_pk] + + for acc in accounts: + try: + account_info = client.get_account_info(acc) + #print(account_info) + print(f"Sols in {str(acc)}",account_info.value.lamports / 10 ** 8) + except Exception: + print("Account not initialized, cannot continue") + return + + + amount = configuration["amount"]["lamports"] order_code = configuration["order_code"] - args = {"amount":int(amount), "order_code":int(order_code)} + args = {"amount":int(amount), + "order_code":int(order_code)} SEEDS = [b"escrow", bytes(business_pk), @@ -59,19 +88,61 @@ async def main(): "to":influencer_pk } - opts = TxOpts(skip_confirmation = True, - skip_preflight = True, + client = select_client(network=network, async_client=False) + + latest_blockhash = client.get_latest_blockhash().value.last_valid_block_height + + + opts = TxOpts(skip_confirmation = False, + skip_preflight = False, + max_retries=1, + last_valid_block_height=latest_blockhash, preflight_commitment="processed") + print_tittle("Instruction description") + ix = create_escrow(args, accounts, program_id=PROGRAM_ID) - - signers = [business] + signers = [business] sign_status = await sign_and_send_transaction(ix, signers, network) + ## forming the transaction + from solana.transaction import Transaction + from solana.rpc.core import RPCException + from solders.compute_budget import set_compute_unit_limit, set_compute_unit_price + + tx = Transaction(fee_payer=business_pk, recent_blockhash=None) \ + .add(set_compute_unit_price(1_000)) \ + .add(ix) - print(sign_status) + #sign_status = await sign_and_send_transaction(ix, signers, opts, network) + #sign_status = sign_and_send_transaction_sync(ix, signers, opts, network) + + try: + print("Network selected: ",network) + client = select_client(network=network, async_client=False) + + #print("Simulate Transaction") + #simulate_response = client.simulate_transaction(tx, sig_verify=True) + #print("Simulate Response", simulate_response) + #print(simulate_response.value.err) + + print("Start Sending transactions with options", opts) + + tx_res = client.send_transaction(tx, *signers, opts=opts) + + print("Client Response tx signature: ", tx_res) + + print("Waiting for transaction confirmation") + + signature_status = client.confirm_transaction(tx_res.value) + + print("Confirm Transaction Status Value:", signature_status) + return signature_status.to_json() + except RPCException as e: + raise RPCException(f"RPC exception happened: {e}") + + -import asyncio asyncio.run(main()) - \ No newline at end of file + diff --git a/solana-python/launch_create_escrow_spl.py b/solana-python/launch_create_escrow_spl.py index fb497981..862e018a 100644 --- a/solana-python/launch_create_escrow_spl.py +++ b/solana-python/launch_create_escrow_spl.py @@ -54,7 +54,9 @@ async def main(): account_info = await get_token_account_info(ata, network) print(f"ATA account {ata} --> amount tokens = {account_info.amount}") - business, business_pk = get_local_keypair_pubkey(path=keypair_paths.bussines_keypair) + #business, business_pk = get_local_keypair_pubkey(path=keypair_paths.bussines_keypair) + business, business_pk = get_local_keypair_pubkey(path=keypair_paths.bussines_GQRD) + _, influencer_pk = get_local_keypair_pubkey(path=keypair_paths.influencer_keypair) _, validation_authority_pk = get_local_keypair_pubkey(path=keypair_paths.validation_authority) @@ -71,6 +73,10 @@ async def main(): # find pdas for create escrow with spl order_code = configuration["order_code"] + + print("order code selected:",order_code) + + SEEDS = [b"token-seed", bytes(str(order_code),"UTF-8")] diff --git a/solana-python/launch_validate.py b/solana-python/launch_validate.py index b8861db5..1c47cd8f 100644 --- a/solana-python/launch_validate.py +++ b/solana-python/launch_validate.py @@ -5,8 +5,9 @@ from solders.keypair import Keypair from solders.pubkey import Pubkey + from pyxfluencer.instructions import validate_escrow_sol -from pyxfluencer.utils import get_local_keypair_pubkey +from pyxfluencer.utils import get_local_keypair_pubkey, select_client, sign_and_send_transaction_sync from pyxfluencer.utils import sign_and_send_transaction from pyxfluencer.program_id import PROGRAM_ID from pyxfluencer import EscrowValidator @@ -86,9 +87,9 @@ def error(self, message): parser.add_argument('--target', choices=['cancel', 'deliver']) args = parser.parse_args() if args.target == 'cancel': - target_state = TargetState.CANCEL + asyncio.run(main(TargetState.CANCEL)) elif args.target == 'deliver': - target_state = TargetState.DELIVERY + asyncio.run(main(TargetState.DELIVERY)) else: print("Incorrect Option") parser.print_help() diff --git a/solana-python/pyxfluencer/utils.py b/solana-python/pyxfluencer/utils.py index 8fcde042..b2000e62 100644 --- a/solana-python/pyxfluencer/utils.py +++ b/solana-python/pyxfluencer/utils.py @@ -1,14 +1,17 @@ import os import json from typing import List + from solders.keypair import Keypair from solders.pubkey import Pubkey +from solders.compute_budget import set_compute_unit_limit, set_compute_unit_price from solana.rpc.api import Client # type: ignore from solana.rpc.async_api import AsyncClient # type: ignore from solana.transaction import Transaction # type: ignore from solana.rpc.core import RPCException # type: ignore +from solana.rpc.api import Client from anchorpy import Wallet, Provider from anchorpy.utils import token diff --git a/solana-python/requirements.txt b/solana-python/requirements.txt index 9cef5236..9c849c29 100644 --- a/solana-python/requirements.txt +++ b/solana-python/requirements.txt @@ -1,2 +1,2 @@ -anchorpy[cli] == 0.19.1 +anchorpy[cli] == 0.20.0 setuptools == 59.6.0 diff --git a/solana-python/xfluencer.json b/solana-python/xfluencer.json new file mode 120000 index 00000000..f2e6f8f0 --- /dev/null +++ b/solana-python/xfluencer.json @@ -0,0 +1 @@ +../solana/target/idl/xfluencer.json \ No newline at end of file diff --git a/solana/Anchor.toml b/solana/Anchor.toml index 74a9eb66..51f2c35d 100644 --- a/solana/Anchor.toml +++ b/solana/Anchor.toml @@ -5,7 +5,7 @@ seeds = false skip-lint = false [programs.localnet] -xfluencer = "GxojWLjFSxSaCBBDzu6ynZ8QTmwUDtN65TMEtNF6P8ig" +xfluencer = "DmYaabL1PhacWWsRwyZpBqBP9n7tVq7115zG2tznYLb9" [programs.devnet] xfluencer = "DmYaabL1PhacWWsRwyZpBqBP9n7tVq7115zG2tznYLb9" @@ -17,7 +17,7 @@ xfluencer = "DmYaabL1PhacWWsRwyZpBqBP9n7tVq7115zG2tznYLb9" url = "https://api.apr.dev" [provider] -cluster = "mainnet" +cluster = "localnet" wallet = "/home/ruben/.config/solana/id.json" [scripts] diff --git a/solana/README.md b/solana/README.md index 78d061ef..1c6a443f 100644 --- a/solana/README.md +++ b/solana/README.md @@ -49,6 +49,14 @@ Check you have the right version `0.29.0` anchor --version ``` +## Configuration + +Check `Anchor.toml` file has the right configuration. + +* Make sure program ids correspond to its network. +* Setup the right provider for deployment, normally it should be `localnet` whend development. +* Scripts define the test that will be run when you type `anchor test` + ## Build To build the Solana program, run the following command: @@ -69,7 +77,7 @@ Previous outcome should match the program id specied at `lib.rs` line `declare_i ## Test -Trigger the TS Solana Program test using +Trigger the Type Script Solana Program test using the following command: ```bash anchor test diff --git a/solana/programs/xfluencer/src/lib.rs b/solana/programs/xfluencer/src/lib.rs index 92c3d72b..00b77de7 100644 --- a/solana/programs/xfluencer/src/lib.rs +++ b/solana/programs/xfluencer/src/lib.rs @@ -60,7 +60,7 @@ pub mod xfluencer { amount: u64, order_code: u64 ) -> ProgramResult { - processor::create_escrow_solana::process(ctx, amount, order_code) + processor::create_escrow_sol::process(ctx, amount, order_code) } pub fn claim_escrow(ctx: Context<ClaimEscrowSolana>, diff --git a/solana/programs/xfluencer/src/processor/create_escrow_solana.rs b/solana/programs/xfluencer/src/processor/create_escrow_sol.rs similarity index 100% rename from solana/programs/xfluencer/src/processor/create_escrow_solana.rs rename to solana/programs/xfluencer/src/processor/create_escrow_sol.rs diff --git a/solana/programs/xfluencer/src/processor/mod.rs b/solana/programs/xfluencer/src/processor/mod.rs index 5f0edd2d..a600077f 100644 --- a/solana/programs/xfluencer/src/processor/mod.rs +++ b/solana/programs/xfluencer/src/processor/mod.rs @@ -1,5 +1,5 @@ pub mod initialize_escrow; -pub mod create_escrow_solana; +pub mod create_escrow_sol; pub mod cancel; pub mod claim; pub mod claim_escrow; diff --git a/solana/programs/xfluencer/src/processor/validate_escrow_sol.rs b/solana/programs/xfluencer/src/processor/validate_escrow_sol.rs index d096c8e6..1ac13fca 100644 --- a/solana/programs/xfluencer/src/processor/validate_escrow_sol.rs +++ b/solana/programs/xfluencer/src/processor/validate_escrow_sol.rs @@ -47,6 +47,13 @@ pub fn process( ctx.accounts.escrow_account.status = target_state; + if target_state == 1 { + msg!( + "Escrow has changed its state to cancel (1)" + ); + + } + // in case of state is delivered, transfer funds to the validation_authority if target_state == 2 { msg!( @@ -54,14 +61,69 @@ pub fn process( &percentage_fee.to_string() ); - if percentage_fee > 1000 { - // 10 % + if percentage_fee > 500 { + // 5 % return err!(CustomError::PercentageFeeOutOfrange)?; } let escrow_amount: u64 = ctx.accounts.escrow_account.get_lamports(); - let fees_amount: u64 = match mul_div_u64(escrow_amount, percentage_fee as u64, 10000 as u64) + /* + + Assuming the amount passed to the escrow already includes the fees: + + We need to break down this total amount into two parts: the payment amount for service X, + + and the fee (denoted as 'r') set by the platform for service X. + + Therefore, the input amount sent to the escrow (denoted as Y) will be composed as follows: + + Y = X + r * X, + + where: + - 'r' represents the fee (0 <= r <= 0.05) set by the platform. + - 'X' is the amount to be given to the influencer. + + Given the total escrow amount (Y) and the fee 'r', how do we determine the fee (denoted as 'F') + + applied to the payment for service X that the contract should send to the authority? + + Let's factor out the common term 'X': + + Y = X + r * X, + + which simplifies to: + + Y = X * (1 + r). + + This implies that the service amount 'X' can be calculated as: + + X = Y / (1 + r). + + Hence, the fee applied to the payment is: + + F = X * r = Y * r / (1 + r). + + The amount 'F' represents what the contract will remit to the authority + + for each successfully validated escrow release. + + It's important to note that in Rust, we will utilize lossless calculations using 'u64' types. + + For this purpose, we'll employ a custom math function, 'mul_div_u64', + + which performs multiplication first and then division using 'u64' types. + + The fee 'r' computed by this instruction should be an integer between 0 and 500, + + corresponding to 0% and 5%, respectively, and not a floating point type. + + Notice that the amount 10000 for 'r' will correspond to 100%. + + */ + + + let fees_amount: u64 = match mul_div_u64(escrow_amount, percentage_fee as u64, (10000 + percentage_fee) as u64) { Some(fees_amount) => { if escrow_amount < fees_amount { @@ -77,17 +139,22 @@ pub fn process( let to_account = ctx.accounts.validation_authority.to_account_info(); msg!( - "Fees to transfer ({} lamports) from escrow to validation authority", + "Amount transferred ({} lamports) from escrow to the fees authority due to fees", &fees_amount.to_string() ); + **from_account.try_borrow_mut_lamports()? -= fees_amount; **to_account.try_borrow_mut_lamports()? += fees_amount; msg!( - "Remaining lamports in Escrow {}", + "Remaining lamports in Escrow {} for the influencer", from_account.get_lamports().to_string() ); + msg!( + "Escrow has changed its state to delivered (2)" + ) + } Ok(()) diff --git a/solana/programs/xfluencer/src/processor/validate_escrow_spl.rs b/solana/programs/xfluencer/src/processor/validate_escrow_spl.rs index 341e7ed6..e625fd19 100644 --- a/solana/programs/xfluencer/src/processor/validate_escrow_spl.rs +++ b/solana/programs/xfluencer/src/processor/validate_escrow_spl.rs @@ -18,44 +18,59 @@ enum EscrowState { Delivered } +impl EscrowState { + fn from_u8(value: u8) -> EscrowState { + match value { + 0 => EscrowState::New, + 1 => EscrowState::Canceled, + 2 => EscrowState::Delivered, + _ => panic!("Unknown value: {}", value), + } + } +} + + pub fn process( ctx: Context<ValidateEscrowSpl>, target_state: u8, percentage_fee: u16, ) -> ProgramResult { - let current_state = ctx.accounts.escrow_account.status; + let current_state_raw = ctx.accounts.escrow_account.status; msg!( "Validating Escrow for SPL token From Current ({}) to Target ({}) state", - current_state, + current_state_raw, target_state ); + ctx.accounts.escrow_account.status = target_state; + + let target = EscrowState::from_u8(target_state); + let current = EscrowState::from_u8(current_state_raw); + // @TODO: Use enumerator instead u8 // Valid State Transitions // 0 -> 1 From New to Cancel // 0 -> 2 From New to Delivered - let cancel_state: u8 = 1; - let delivered_state: u8 = 2; + //let cancel_state = EscrowState::from_u8(1); + //let delivered_state = EscrowState::from_u8(2); - if current_state == cancel_state { + if current == EscrowState::Canceled { return err!(CustomError::EscrowAlreadyCancel)?; } - if current_state == delivered_state { + if current == EscrowState::Delivered { return err!(CustomError::EscrowAlreadyReleased)?; } - if target_state != cancel_state && target_state != delivered_state { + if target != EscrowState::Canceled && target != EscrowState::Delivered { return err!(CustomError::BadTargetStateForEscrow)?; } - ctx.accounts.escrow_account.status = target_state; - // in case of state is delivered, transfer funds to the validation_authority - if target_state == delivered_state { + if target == EscrowState::Delivered { msg!( "Percentage Fee to apply By Xfluencer Platform: {} (2 decimal places)", &percentage_fee.to_string() diff --git a/solana/tests/escrow_sol.ts b/solana/tests/escrow_sol.ts index 7e5c6993..8adb30a8 100644 --- a/solana/tests/escrow_sol.ts +++ b/solana/tests/escrow_sol.ts @@ -101,7 +101,7 @@ describe("Testing Escrow for SOL", () => { }); - it('Create Escrow for SOL, Validate for Cancellation, and Claim Cancel By Business', async () => { + it('Create Escrow for SOL, Validate to Cancel, and Claim Cancel By Business', async () => { const orderCode = 2; @@ -130,7 +130,6 @@ describe("Testing Escrow for SOL", () => { /////////////////// // create escrow // /////////////////// - await program.methods .createEscrow( new anchor.BN(amount), @@ -219,7 +218,7 @@ describe("Testing Escrow for SOL", () => { program.programId ); - const amount = 10 ** 9; // 1 SOL + const amount = 1.05 * 10 ** 9; // 1 SOL const options = { skipPreflight: true @@ -245,7 +244,7 @@ describe("Testing Escrow for SOL", () => { const escrowAccount = await program.account.escrowAccountSolana.fetch(escrowPDA) const escrow_value = escrowAccount.amount.toNumber(); - console.log("Amount in escrow after creation",escrow_value) + console.log("Amount in Escrow After Creation",escrow_value) assert.ok(escrow_value == amount); ///////////////////// @@ -274,7 +273,8 @@ describe("Testing Escrow for SOL", () => { const account_validator = await provider.connection.getBalanceAndContext(validationAuthorityPublicKey); const fees_collected = account_validator.value; - console.log("Fees collected by validation authority", fees_collected) + console.log("Fees collected by validation authority", fees_collected, amount * percentage_fee / (percentage_fee + 10000)) + assert.ok(fees_collected >= amount * percentage_fee / (percentage_fee + 10000)); const account_escrow = await provider.connection.getBalanceAndContext(escrowPDA); const remaining = account_escrow.value; @@ -373,14 +373,14 @@ describe("Testing Escrow for SOL", () => { ///////////////////// // validate escrow // ///////////////////// - const state = 3; // 3 is a bad state transiction (only 1 or 2) - const percentage_fee = 500; + const targetState = 3; // 3 is a bad state transiction (only taget states 1 and 2 are allowed) + const percentageFee = 500; try { await program.methods .validateEscrowSol( - state, - percentage_fee + targetState, + percentageFee ) .accounts( {