Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Escrow smart contract #2

Open
hkirat opened this issue Mar 18, 2024 · 4 comments
Open

Escrow smart contract #2

hkirat opened this issue Mar 18, 2024 · 4 comments

Comments

@hkirat
Copy link
Contributor

hkirat commented Mar 18, 2024

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
Copy link
Contributor Author

hkirat commented Mar 18, 2024

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%)

@0xtarunkm
Copy link

can you assign this issue to me

@mayankthinks
Copy link

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
Copy link

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,
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants