examples: Add escrow (#410)
This commit is contained in:
parent
3c295f958a
commit
fcb7a3e17b
|
@ -67,6 +67,7 @@ jobs:
|
||||||
- <<: *examples
|
- <<: *examples
|
||||||
name: Runs the examples 3
|
name: Runs the examples 3
|
||||||
script:
|
script:
|
||||||
|
- pushd examples/escrow && yarn && anchor test && popd
|
||||||
- pushd examples/pyth && yarn && anchor test && popd
|
- pushd examples/pyth && yarn && anchor test && popd
|
||||||
- pushd examples/tutorial/basic-0 && anchor test && popd
|
- pushd examples/tutorial/basic-0 && anchor test && popd
|
||||||
- pushd examples/tutorial/basic-1 && anchor test && popd
|
- pushd examples/tutorial/basic-1 && anchor test && popd
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
[provider]
|
||||||
|
cluster = "localnet"
|
||||||
|
wallet = "~/.config/solana/id.json"
|
|
@ -0,0 +1,4 @@
|
||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"programs/*"
|
||||||
|
]
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"@project-serum/anchor": "^0.9.0",
|
||||||
|
"@project-serum/serum": "0.13.38",
|
||||||
|
"@solana/web3.js": "^1.18.0",
|
||||||
|
"@solana/spl-token": "^0.1.6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"ts-mocha": "^8.0.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
[package]
|
||||||
|
name = "escrow"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Created with Anchor"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "lib"]
|
||||||
|
name = "escrow"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
no-entrypoint = []
|
||||||
|
no-idl = []
|
||||||
|
cpi = ["no-entrypoint"]
|
||||||
|
default = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anchor-lang = { path = "../../../../lang" }
|
||||||
|
anchor-spl = { path = "../../../../spl" }
|
||||||
|
spl-token = { version = "3.1.1", features = ["no-entrypoint"] }
|
|
@ -0,0 +1,2 @@
|
||||||
|
[target.bpfel-unknown-unknown.dependencies.std]
|
||||||
|
features = []
|
|
@ -0,0 +1,228 @@
|
||||||
|
//! An example of an escrow program, inspired by PaulX tutorial seen here
|
||||||
|
//! https://paulx.dev/blog/2021/01/14/programming-on-solana-an-introduction/
|
||||||
|
//! This example has some changes to implementation, but more or less should be the same overall
|
||||||
|
//! Also gives examples on how to use some newer anchor features and CPI
|
||||||
|
//!
|
||||||
|
//! User (Initializer) constructs an escrow deal:
|
||||||
|
//! - SPL token (X) they will offer and amount
|
||||||
|
//! - SPL token (Y) count they want in return and amount
|
||||||
|
//! - Program will take ownership of initializer's token X account
|
||||||
|
//!
|
||||||
|
//! Once this escrow is initialised, either:
|
||||||
|
//! 1. User (Taker) can call the exchange function to exchange their Y for X
|
||||||
|
//! - This will close the escrow account and no longer be usable
|
||||||
|
//! OR
|
||||||
|
//! 2. If no one has exchanged, the initializer can close the escrow account
|
||||||
|
//! - Initializer will get back ownership of their token X account
|
||||||
|
|
||||||
|
use anchor_lang::prelude::*;
|
||||||
|
use anchor_spl::token::{self, SetAuthority, TokenAccount, Transfer};
|
||||||
|
use spl_token::instruction::AuthorityType;
|
||||||
|
|
||||||
|
#[program]
|
||||||
|
pub mod escrow {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn initialize_escrow(
|
||||||
|
ctx: Context<InitializeEscrow>,
|
||||||
|
initializer_amount: u64,
|
||||||
|
taker_amount: u64,
|
||||||
|
) -> ProgramResult {
|
||||||
|
ctx.accounts.escrow_account.initializer_key = *ctx.accounts.initializer.key;
|
||||||
|
ctx.accounts
|
||||||
|
.escrow_account
|
||||||
|
.initializer_deposit_token_account = *ctx
|
||||||
|
.accounts
|
||||||
|
.initializer_deposit_token_account
|
||||||
|
.to_account_info()
|
||||||
|
.key;
|
||||||
|
ctx.accounts
|
||||||
|
.escrow_account
|
||||||
|
.initializer_receive_token_account = *ctx
|
||||||
|
.accounts
|
||||||
|
.initializer_receive_token_account
|
||||||
|
.to_account_info()
|
||||||
|
.key;
|
||||||
|
ctx.accounts.escrow_account.initializer_amount = initializer_amount;
|
||||||
|
ctx.accounts.escrow_account.taker_amount = taker_amount;
|
||||||
|
|
||||||
|
let (pda, _bump_seed) = Pubkey::find_program_address(&[b"escrow"], ctx.program_id);
|
||||||
|
token::set_authority(ctx.accounts.into(), AuthorityType::AccountOwner, Some(pda))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cancel_escrow(ctx: Context<CancelEscrow>) -> ProgramResult {
|
||||||
|
let (_pda, bump_seed) = Pubkey::find_program_address(&[b"escrow"], ctx.program_id);
|
||||||
|
let seeds = &[&b"escrow"[..], &[bump_seed]];
|
||||||
|
|
||||||
|
token::set_authority(
|
||||||
|
ctx.accounts
|
||||||
|
.into_set_authority_context()
|
||||||
|
.with_signer(&[&seeds[..]]),
|
||||||
|
AuthorityType::AccountOwner,
|
||||||
|
Some(ctx.accounts.escrow_account.initializer_key),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exchange(ctx: Context<Exchange>) -> ProgramResult {
|
||||||
|
// Transferring from initializer to taker
|
||||||
|
let (_pda, bump_seed) = Pubkey::find_program_address(&[b"escrow"], ctx.program_id);
|
||||||
|
let seeds = &[&b"escrow"[..], &[bump_seed]];
|
||||||
|
|
||||||
|
token::transfer(
|
||||||
|
ctx.accounts
|
||||||
|
.into_transfer_to_taker_context()
|
||||||
|
.with_signer(&[&seeds[..]]),
|
||||||
|
ctx.accounts.escrow_account.initializer_amount,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
token::transfer(
|
||||||
|
ctx.accounts.into_transfer_to_initializer_context(),
|
||||||
|
ctx.accounts.escrow_account.taker_amount,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
token::set_authority(
|
||||||
|
ctx.accounts
|
||||||
|
.into_set_authority_context()
|
||||||
|
.with_signer(&[&seeds[..]]),
|
||||||
|
AuthorityType::AccountOwner,
|
||||||
|
Some(ctx.accounts.escrow_account.initializer_key),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Accounts)]
|
||||||
|
#[instruction(initializer_amount: u64)]
|
||||||
|
pub struct InitializeEscrow<'info> {
|
||||||
|
#[account(signer)]
|
||||||
|
pub initializer: AccountInfo<'info>,
|
||||||
|
#[account(
|
||||||
|
mut,
|
||||||
|
constraint = initializer_deposit_token_account.amount >= initializer_amount
|
||||||
|
)]
|
||||||
|
pub initializer_deposit_token_account: CpiAccount<'info, TokenAccount>,
|
||||||
|
pub initializer_receive_token_account: CpiAccount<'info, TokenAccount>,
|
||||||
|
#[account(init)]
|
||||||
|
pub escrow_account: ProgramAccount<'info, EscrowAccount>,
|
||||||
|
pub token_program: AccountInfo<'info>,
|
||||||
|
pub rent: Sysvar<'info, Rent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Accounts)]
|
||||||
|
pub struct Exchange<'info> {
|
||||||
|
#[account(signer)]
|
||||||
|
pub taker: AccountInfo<'info>,
|
||||||
|
#[account(mut)]
|
||||||
|
pub taker_deposit_token_account: CpiAccount<'info, TokenAccount>,
|
||||||
|
#[account(mut)]
|
||||||
|
pub taker_receive_token_account: CpiAccount<'info, TokenAccount>,
|
||||||
|
#[account(mut)]
|
||||||
|
pub pda_deposit_token_account: CpiAccount<'info, TokenAccount>,
|
||||||
|
#[account(mut)]
|
||||||
|
pub initializer_receive_token_account: CpiAccount<'info, TokenAccount>,
|
||||||
|
#[account(mut)]
|
||||||
|
pub initializer_main_account: AccountInfo<'info>,
|
||||||
|
#[account(
|
||||||
|
mut,
|
||||||
|
constraint = escrow_account.taker_amount <= taker_deposit_token_account.amount,
|
||||||
|
constraint = escrow_account.initializer_deposit_token_account == *pda_deposit_token_account.to_account_info().key,
|
||||||
|
constraint = escrow_account.initializer_receive_token_account == *initializer_receive_token_account.to_account_info().key,
|
||||||
|
constraint = escrow_account.initializer_key == *initializer_main_account.key,
|
||||||
|
close = initializer_main_account
|
||||||
|
)]
|
||||||
|
pub escrow_account: ProgramAccount<'info, EscrowAccount>,
|
||||||
|
pub pda_account: AccountInfo<'info>,
|
||||||
|
pub token_program: AccountInfo<'info>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Accounts)]
|
||||||
|
pub struct CancelEscrow<'info> {
|
||||||
|
pub initializer: AccountInfo<'info>,
|
||||||
|
#[account(mut)]
|
||||||
|
pub pda_deposit_token_account: CpiAccount<'info, TokenAccount>,
|
||||||
|
pub pda_account: AccountInfo<'info>,
|
||||||
|
#[account(
|
||||||
|
mut,
|
||||||
|
constraint = escrow_account.initializer_key == *initializer.key,
|
||||||
|
constraint = escrow_account.initializer_deposit_token_account == *pda_deposit_token_account.to_account_info().key,
|
||||||
|
close = initializer
|
||||||
|
)]
|
||||||
|
pub escrow_account: ProgramAccount<'info, EscrowAccount>,
|
||||||
|
pub token_program: AccountInfo<'info>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[account]
|
||||||
|
pub struct EscrowAccount {
|
||||||
|
pub initializer_key: Pubkey,
|
||||||
|
pub initializer_deposit_token_account: Pubkey,
|
||||||
|
pub initializer_receive_token_account: Pubkey,
|
||||||
|
pub initializer_amount: u64,
|
||||||
|
pub taker_amount: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'info> From<&mut InitializeEscrow<'info>>
|
||||||
|
for CpiContext<'_, '_, '_, 'info, SetAuthority<'info>>
|
||||||
|
{
|
||||||
|
fn from(accounts: &mut InitializeEscrow<'info>) -> Self {
|
||||||
|
let cpi_accounts = SetAuthority {
|
||||||
|
account_or_mint: accounts
|
||||||
|
.initializer_deposit_token_account
|
||||||
|
.to_account_info()
|
||||||
|
.clone(),
|
||||||
|
current_authority: accounts.initializer.clone(),
|
||||||
|
};
|
||||||
|
let cpi_program = accounts.token_program.clone();
|
||||||
|
CpiContext::new(cpi_program, cpi_accounts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'info> CancelEscrow<'info> {
|
||||||
|
fn into_set_authority_context(&self) -> CpiContext<'_, '_, '_, 'info, SetAuthority<'info>> {
|
||||||
|
let cpi_accounts = SetAuthority {
|
||||||
|
account_or_mint: self.pda_deposit_token_account.to_account_info().clone(),
|
||||||
|
current_authority: self.pda_account.clone(),
|
||||||
|
};
|
||||||
|
CpiContext::new(self.token_program.clone(), cpi_accounts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'info> Exchange<'info> {
|
||||||
|
fn into_set_authority_context(&self) -> CpiContext<'_, '_, '_, 'info, SetAuthority<'info>> {
|
||||||
|
let cpi_accounts = SetAuthority {
|
||||||
|
account_or_mint: self.pda_deposit_token_account.to_account_info().clone(),
|
||||||
|
current_authority: self.pda_account.clone(),
|
||||||
|
};
|
||||||
|
CpiContext::new(self.token_program.clone(), cpi_accounts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'info> Exchange<'info> {
|
||||||
|
fn into_transfer_to_taker_context(&self) -> CpiContext<'_, '_, '_, 'info, Transfer<'info>> {
|
||||||
|
let cpi_accounts = Transfer {
|
||||||
|
from: self.pda_deposit_token_account.to_account_info().clone(),
|
||||||
|
to: self.taker_receive_token_account.to_account_info().clone(),
|
||||||
|
authority: self.pda_account.clone(),
|
||||||
|
};
|
||||||
|
CpiContext::new(self.token_program.clone(), cpi_accounts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'info> Exchange<'info> {
|
||||||
|
fn into_transfer_to_initializer_context(
|
||||||
|
&self,
|
||||||
|
) -> CpiContext<'_, '_, '_, 'info, Transfer<'info>> {
|
||||||
|
let cpi_accounts = Transfer {
|
||||||
|
from: self.taker_deposit_token_account.to_account_info().clone(),
|
||||||
|
to: self
|
||||||
|
.initializer_receive_token_account
|
||||||
|
.to_account_info()
|
||||||
|
.clone(),
|
||||||
|
authority: self.taker.clone(),
|
||||||
|
};
|
||||||
|
CpiContext::new(self.token_program.clone(), cpi_accounts)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,207 @@
|
||||||
|
const anchor = require("@project-serum/anchor");
|
||||||
|
const { TOKEN_PROGRAM_ID, Token } = require("@solana/spl-token");
|
||||||
|
const assert = require("assert");
|
||||||
|
|
||||||
|
describe("escrow", () => {
|
||||||
|
const provider = anchor.Provider.env();
|
||||||
|
anchor.setProvider(provider);
|
||||||
|
|
||||||
|
const program = anchor.workspace.Escrow;
|
||||||
|
|
||||||
|
let mintA = null;
|
||||||
|
let mintB = null;
|
||||||
|
let initializerTokenAccountA = null;
|
||||||
|
let initializerTokenAccountB = null;
|
||||||
|
let takerTokenAccountA = null;
|
||||||
|
let takerTokenAccountB = null;
|
||||||
|
let pda = null;
|
||||||
|
|
||||||
|
const takerAmount = 1000;
|
||||||
|
const initializerAmount = 500;
|
||||||
|
|
||||||
|
const escrowAccount = anchor.web3.Keypair.generate();
|
||||||
|
const payer = anchor.web3.Keypair.generate();
|
||||||
|
const mintAuthority = anchor.web3.Keypair.generate();
|
||||||
|
|
||||||
|
it("Initialise escrow state", async () => {
|
||||||
|
// Airdropping tokens to a payer.
|
||||||
|
await provider.connection.confirmTransaction(
|
||||||
|
await provider.connection.requestAirdrop(payer.publicKey, 10000000000),
|
||||||
|
"confirmed"
|
||||||
|
);
|
||||||
|
|
||||||
|
mintA = await Token.createMint(
|
||||||
|
provider.connection,
|
||||||
|
payer,
|
||||||
|
mintAuthority.publicKey,
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
TOKEN_PROGRAM_ID
|
||||||
|
);
|
||||||
|
|
||||||
|
mintB = await Token.createMint(
|
||||||
|
provider.connection,
|
||||||
|
payer,
|
||||||
|
mintAuthority.publicKey,
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
TOKEN_PROGRAM_ID
|
||||||
|
);
|
||||||
|
|
||||||
|
initializerTokenAccountA = await mintA.createAccount(provider.wallet.publicKey);
|
||||||
|
takerTokenAccountA = await mintA.createAccount(provider.wallet.publicKey);
|
||||||
|
|
||||||
|
initializerTokenAccountB = await mintB.createAccount(provider.wallet.publicKey);
|
||||||
|
takerTokenAccountB = await mintB.createAccount(provider.wallet.publicKey);
|
||||||
|
|
||||||
|
await mintA.mintTo(
|
||||||
|
initializerTokenAccountA,
|
||||||
|
mintAuthority.publicKey,
|
||||||
|
[mintAuthority],
|
||||||
|
initializerAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
await mintB.mintTo(
|
||||||
|
takerTokenAccountB,
|
||||||
|
mintAuthority.publicKey,
|
||||||
|
[mintAuthority],
|
||||||
|
takerAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
let _initializerTokenAccountA = await mintA.getAccountInfo(initializerTokenAccountA);
|
||||||
|
let _takerTokenAccountB = await mintB.getAccountInfo(takerTokenAccountB);
|
||||||
|
|
||||||
|
assert.ok(_initializerTokenAccountA.amount.toNumber() == initializerAmount);
|
||||||
|
assert.ok(_takerTokenAccountB.amount.toNumber() == takerAmount);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Initialize escrow", async () => {
|
||||||
|
await program.rpc.initializeEscrow(
|
||||||
|
new anchor.BN(initializerAmount),
|
||||||
|
new anchor.BN(takerAmount),
|
||||||
|
{
|
||||||
|
accounts: {
|
||||||
|
initializer: provider.wallet.publicKey,
|
||||||
|
initializerDepositTokenAccount: initializerTokenAccountA,
|
||||||
|
initializerReceiveTokenAccount: initializerTokenAccountB,
|
||||||
|
escrowAccount: escrowAccount.publicKey,
|
||||||
|
tokenProgram: TOKEN_PROGRAM_ID,
|
||||||
|
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
|
||||||
|
},
|
||||||
|
instructions: [
|
||||||
|
await program.account.escrowAccount.createInstruction(escrowAccount),
|
||||||
|
],
|
||||||
|
signers: [escrowAccount],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get the PDA that is assigned authority to token account.
|
||||||
|
const [_pda, _nonce] = await anchor.web3.PublicKey.findProgramAddress(
|
||||||
|
[Buffer.from(anchor.utils.bytes.utf8.encode("escrow"))],
|
||||||
|
program.programId
|
||||||
|
);
|
||||||
|
|
||||||
|
pda = _pda;
|
||||||
|
|
||||||
|
let _initializerTokenAccountA = await mintA.getAccountInfo(initializerTokenAccountA);
|
||||||
|
|
||||||
|
let _escrowAccount = await program.account.escrowAccount.fetch(
|
||||||
|
escrowAccount.publicKey
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check that the new owner is the PDA.
|
||||||
|
assert.ok(_initializerTokenAccountA.owner.equals(pda));
|
||||||
|
|
||||||
|
// Check that the values in the escrow account match what we expect.
|
||||||
|
assert.ok(_escrowAccount.initializerKey.equals(provider.wallet.publicKey));
|
||||||
|
assert.ok(_escrowAccount.initializerAmount.toNumber() == initializerAmount);
|
||||||
|
assert.ok(_escrowAccount.takerAmount.toNumber() == takerAmount);
|
||||||
|
assert.ok(
|
||||||
|
_escrowAccount.initializerDepositTokenAccount.equals(initializerTokenAccountA)
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
_escrowAccount.initializerReceiveTokenAccount.equals(initializerTokenAccountB)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Exchange escrow", async () => {
|
||||||
|
await program.rpc.exchange({
|
||||||
|
accounts: {
|
||||||
|
taker: provider.wallet.publicKey,
|
||||||
|
takerDepositTokenAccount: takerTokenAccountB,
|
||||||
|
takerReceiveTokenAccount: takerTokenAccountA,
|
||||||
|
pdaDepositTokenAccount: initializerTokenAccountA,
|
||||||
|
initializerReceiveTokenAccount: initializerTokenAccountB,
|
||||||
|
initializerMainAccount: provider.wallet.publicKey,
|
||||||
|
escrowAccount: escrowAccount.publicKey,
|
||||||
|
pdaAccount: pda,
|
||||||
|
tokenProgram: TOKEN_PROGRAM_ID,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let _takerTokenAccountA = await mintA.getAccountInfo(takerTokenAccountA);
|
||||||
|
let _takerTokenAccountB = await mintB.getAccountInfo(takerTokenAccountB);
|
||||||
|
let _initializerTokenAccountA = await mintA.getAccountInfo(initializerTokenAccountA);
|
||||||
|
let _initializerTokenAccountB = await mintB.getAccountInfo(initializerTokenAccountB);
|
||||||
|
|
||||||
|
// Check that the initializer gets back ownership of their token account.
|
||||||
|
assert.ok(_takerTokenAccountA.owner.equals(provider.wallet.publicKey));
|
||||||
|
|
||||||
|
assert.ok(_takerTokenAccountA.amount.toNumber() == initializerAmount);
|
||||||
|
assert.ok(_initializerTokenAccountA.amount.toNumber() == 0);
|
||||||
|
assert.ok(_initializerTokenAccountB.amount.toNumber() == takerAmount);
|
||||||
|
assert.ok(_takerTokenAccountB.amount.toNumber() == 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
let newEscrow = anchor.web3.Keypair.generate();
|
||||||
|
|
||||||
|
it("Initialize escrow and cancel escrow", async () => {
|
||||||
|
// Put back tokens into initializer token A account.
|
||||||
|
await mintA.mintTo(
|
||||||
|
initializerTokenAccountA,
|
||||||
|
mintAuthority.publicKey,
|
||||||
|
[mintAuthority],
|
||||||
|
initializerAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
await program.rpc.initializeEscrow(
|
||||||
|
new anchor.BN(initializerAmount),
|
||||||
|
new anchor.BN(takerAmount),
|
||||||
|
{
|
||||||
|
accounts: {
|
||||||
|
initializer: provider.wallet.publicKey,
|
||||||
|
initializerDepositTokenAccount: initializerTokenAccountA,
|
||||||
|
initializerReceiveTokenAccount: initializerTokenAccountB,
|
||||||
|
escrowAccount: newEscrow.publicKey,
|
||||||
|
tokenProgram: TOKEN_PROGRAM_ID,
|
||||||
|
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
|
||||||
|
},
|
||||||
|
instructions: [await program.account.escrowAccount.createInstruction(newEscrow)],
|
||||||
|
signers: [newEscrow],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let _initializerTokenAccountA = await mintA.getAccountInfo(initializerTokenAccountA);
|
||||||
|
|
||||||
|
// Check that the new owner is the PDA.
|
||||||
|
assert.ok(_initializerTokenAccountA.owner.equals(pda));
|
||||||
|
|
||||||
|
// Cancel the escrow.
|
||||||
|
await program.rpc.cancelEscrow({
|
||||||
|
accounts: {
|
||||||
|
initializer: provider.wallet.publicKey,
|
||||||
|
pdaDepositTokenAccount: initializerTokenAccountA,
|
||||||
|
pdaAccount: pda,
|
||||||
|
escrowAccount: newEscrow.publicKey,
|
||||||
|
tokenProgram: TOKEN_PROGRAM_ID,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check the final owner should be the provider public key.
|
||||||
|
_initializerTokenAccountA = await mintA.getAccountInfo(initializerTokenAccountA);
|
||||||
|
assert.ok(_initializerTokenAccountA.owner.equals(provider.wallet.publicKey));
|
||||||
|
|
||||||
|
// Check all the funds are still there.
|
||||||
|
assert.ok(_initializerTokenAccountA.amount.toNumber() == initializerAmount);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue