code100x / decentralized-fiverr

As the name suggests, we need a simple decentralized marketplace where payments are escrowed and dispensed post job completion
69 stars 46 forks source link

Escrow smart contract #2

Open hkirat opened 4 months ago

hkirat commented 4 months ago

A simple smart contract that takes

  1. provider_address
  2. receiver_address
  3. amount as inputs to escrow the amount from the provider and keep it until the task is done

Needs to be done on solana

hkirat commented 4 months ago

Functions need to be

  1. Initiate transfer
  2. Revert transfer (incase the user doesn't finish the task)
  3. Dispense transfer (to dispense the amount to the end user, keep a configurable percentage for the treasury, let's say 5%)
tarunclub commented 4 months ago

can you assign this issue to me

mayankthinks commented 3 months ago

use solana_program::{ account_info::{next_account_info, AccountInfo}, entrypoint, entrypoint::ProgramResult, msg, program_error::ProgramError, pubkey::Pubkey, };

entrypoint!(process_instruction);

fn process_instruction( _program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8], ) -> ProgramResult { if instruction_data.is_empty() { msg!("No instruction provided"); return Err(ProgramError::InvalidInstructionData); }

let (instruction, rest) = instruction_data.split_first().ok_or(ProgramError::InvalidInstructionData)?;

match instruction {
    0 => initiate_transfer(accounts, rest),
    1 => revert_transfer(accounts),
    2 => dispense_transfer(accounts),
    _ => {
        msg!("Invalid instruction");
        Err(ProgramError::InvalidInstructionData)
    }
}

}

fn initiate_transfer(accounts: &[AccountInfo], amount_data: &[u8]) -> ProgramResult { let account_iter = &mut accounts.iter(); let provider_account = next_account_info(account_iter)?; let receiver_account = next_account_info(account_iter)?; let escrow_account = next_account_info(account_iter)?;

let amount = u64::from_le_bytes(*array_ref![amount_data, 0, 8]);

// Ensure provider is a signer
if !provider_account.is_signer {
    msg!("Provider account must be a signer");
    return Err(ProgramError::MissingRequiredSignature);
}

// Ensure the escrow account has enough space
if escrow_account.data_len() < 65 {
    msg!("Escrow account data length is insufficient");
    return Err(ProgramError::InvalidAccountData);
}

// Transfer lamports from provider to escrow
provider_account.try_borrow_mut_lamports()?.checked_sub(amount)
    .ok_or(ProgramError::InsufficientFunds)?;
escrow_account.try_borrow_mut_lamports()?.checked_add(amount)
    .ok_or(ProgramError::InvalidAccountData)?;

// Store provider and receiver addresses in escrow account data
let mut escrow_data = escrow_account.try_borrow_mut_data()?;
escrow_data[0..32].copy_from_slice(&provider_account.key.to_bytes());
escrow_data[32..64].copy_from_slice(&receiver_account.key.to_bytes());
escrow_data[64] = 0; // Task not completed

Ok(())

}

fn revert_transfer(accounts: &[AccountInfo]) -> ProgramResult { let account_iter = &mut accounts.iter(); let provider_account = next_account_info(account_iter)?; let escrow_account = next_account_info(account_iter)?;

// Ensure provider is a signer
if !provider_account.is_signer {
    msg!("Provider account must be a signer");
    return Err(ProgramError::MissingRequiredSignature);
}

// Ensure the escrow account has enough space
if escrow_account.data_len() < 65 {
    msg!("Escrow account data length is insufficient");
    return Err(ProgramError::InvalidAccountData);
}

// Transfer lamports from escrow back to provider
let escrow_data = escrow_account.try_borrow_data()?;
let amount = provider_account.lamports().checked_add(escrow_account.lamports())
    .ok_or(ProgramError::Overflow)?;
provider_account.try_borrow_mut_lamports()?.checked_add(escrow_account.lamports())
    .ok_or(ProgramError::Overflow)?;
**escrow_account.try_borrow_mut_lamports()? = 0;

// Clear provider and receiver addresses from escrow account data
let mut escrow_data = escrow_account.try_borrow_mut_data()?;
escrow_data[0..64].fill(0);

Ok(())

}

fn dispense_transfer(accounts: &[AccountInfo]) -> ProgramResult { let account_iter = &mut accounts.iter(); let provider_account = next_account_info(account_iter)?; let escrow_account = next_account_info(account_iter)?; let treasury_account = next_account_info(account_iter)?;

// Ensure provider is a signer
if !provider_account.is_signer {
    msg!("Provider account must be a signer");
    return Err(ProgramError::MissingRequiredSignature);
}

// Ensure the escrow account has enough space
if escrow_account.data_len() < 65 {
    msg!("Escrow account data length is insufficient");
    return Err(ProgramError::InvalidAccountData);
}

// Calculate amount to dispense and transfer to receiver
let escrow_data = escrow_account.try_borrow_data()?;
let amount = escrow_account.lamports().checked_sub(escrow_account.lamports() / 20) // 5% for treasury
    .ok_or(ProgramError::InsufficientFunds)?;
**escrow_account.try_borrow_mut_lamports()? = 0;
**treasury_account.try_borrow_mut_lamports()? += escrow_account.lamports() / 20;
**provider_account.try_borrow_mut_lamports()? += amount;

// Clear provider and receiver addresses from escrow account data
let mut escrow_data = escrow_account.try_borrow_mut_data()?;
escrow_data[0..64].fill(0);

Ok(())

}

In this code:

initiate_transfer is called to escrow the specified amount from the provider's account. It stores the provider and receiver addresses in the escrow account data. revert_transfer is called to revert the transfer if the task is not completed. It returns the escrowed amount from the escrow account to the provider's account. dispense_transfer is called to dispense the escrowed amount to the receiver's account after deducting 5% for the treasury. It transfers the amount to the receiver's account and sends 5% to the treasury account.

adityakaaltatva commented 3 weeks ago

use anchor_lang::prelude::*;

declare_id!("Fg6PaFpoGXkYsidMpWFKtnpM7K6JZrTAmL5qXXCijMWd");

pub mod solana_escrow { use super::*; pub fn initiate_transfer(ctx: Context, amount: u64) -> ProgramResult { let escrow_account = &mut ctx.accounts.escrow_account; escrow_account.amount = amount; Ok(()) }

pub fn revert_transfer(ctx: Context<RevertTransfer>) -> ProgramResult {
    let escrow_account = &mut ctx.accounts.escrow_account;
    let provider_account = &mut ctx.accounts.provider_account;

    **provider_account.to_account_info().try_borrow_mut_lamports()? += escrow_account.amount;
    **escrow_account.to_account_info().try_borrow_mut_lamports()? -= escrow_account.amount;
    escrow_account.amount = 0;
    Ok(())
}

pub fn dispense_transfer(ctx: Context<DispenseTransfer>) -> ProgramResult {
    let escrow_account = &mut ctx.accounts.escrow_account;
    let receiver_account = &mut ctx.accounts.receiver_account;
    let treasury_account = &mut ctx.accounts.treasury_account;

    let treasury_cut = (escrow_account.amount as f64 * 0.05) as u64;
    let receiver_amount = escrow_account.amount - treasury_cut;

    **receiver_account.to_account_info().try_borrow_mut_lamports()? += receiver_amount;
    **treasury_account.to_account_info().try_borrow_mut_lamports()? += treasury_cut;
    **escrow_account.to_account_info().try_borrow_mut_lamports()? -= escrow_account.amount;
    escrow_account.amount = 0;
    Ok(())
}

}

[derive(Accounts)]

pub struct InitiateTransfer<'info> {

[account(mut)]

pub provider_account: Signer<'info>,
#[account(mut)]
pub escrow_account: Account<'info, EscrowAccount>,

}

[derive(Accounts)]

pub struct RevertTransfer<'info> {

[account(mut)]

pub provider_account: Signer<'info>,
#[account(mut)]
pub escrow_account: Account<'info, EscrowAccount>,

}

[derive(Accounts)]

pub struct DispenseTransfer<'info> {

[account(mut)]

pub escrow_account: Account<'info, EscrowAccount>,
#[account(mut)]
pub receiver_account: AccountInfo<'info>,
#[account(mut)]
pub treasury_account: AccountInfo<'info>,

}

[account]

pub struct EscrowAccount { pub amount: u64, }