lang: Add `Lamports` trait (#2552)
This commit is contained in:
parent
5624bfe0ff
commit
e55cd3e646
|
@ -12,6 +12,8 @@ The minor version will be incremented upon a breaking change and the patch versi
|
|||
|
||||
### Features
|
||||
|
||||
- lang: Add `get_lamports`, `add_lamports` and `sub_lamports` methods for all account types ([#2552](https://github.com/coral-xyz/anchor/pull/2552)).
|
||||
|
||||
### Fixes
|
||||
|
||||
- ts: Packages no longer depend on `assert` ([#2535](https://github.com/coral-xyz/anchor/pull/2535)).
|
||||
|
|
|
@ -137,6 +137,51 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Lamports related utility methods for accounts.
|
||||
pub trait Lamports<'info>: AsRef<AccountInfo<'info>> {
|
||||
/// Get the lamports of the account.
|
||||
fn get_lamports(&self) -> u64 {
|
||||
self.as_ref().lamports()
|
||||
}
|
||||
|
||||
/// Add lamports to the account.
|
||||
///
|
||||
/// This method is useful for transfering lamports from a PDA.
|
||||
///
|
||||
/// # Requirements
|
||||
///
|
||||
/// 1. The account must be marked `mut`.
|
||||
/// 2. The total lamports **before** the transaction must equal to total lamports **after**
|
||||
/// the transaction.
|
||||
/// 3. `lamports` field of the account info should not currently be borrowed.
|
||||
///
|
||||
/// See [`Lamports::sub_lamports`] for subtracting lamports.
|
||||
fn add_lamports(&self, amount: u64) -> Result<&Self> {
|
||||
**self.as_ref().try_borrow_mut_lamports()? += amount;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Subtract lamports from the account.
|
||||
///
|
||||
/// This method is useful for transfering lamports from a PDA.
|
||||
///
|
||||
/// # Requirements
|
||||
///
|
||||
/// 1. The account must be owned by the executing program.
|
||||
/// 2. The account must be marked `mut`.
|
||||
/// 3. The total lamports **before** the transaction must equal to total lamports **after**
|
||||
/// the transaction.
|
||||
/// 4. `lamports` field of the account info should not currently be borrowed.
|
||||
///
|
||||
/// See [`Lamports::add_lamports`] for adding lamports.
|
||||
fn sub_lamports(&self, amount: u64) -> Result<&Self> {
|
||||
**self.as_ref().try_borrow_mut_lamports()? -= amount;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'info, T: AsRef<AccountInfo<'info>>> Lamports<'info> for T {}
|
||||
|
||||
/// A data structure that can be serialized and stored into account storage,
|
||||
/// i.e. an
|
||||
/// [`AccountInfo`](../solana_program/account_info/struct.AccountInfo.html#structfield.data)'s
|
||||
|
@ -300,8 +345,8 @@ pub mod prelude {
|
|||
require, require_eq, require_gt, require_gte, require_keys_eq, require_keys_neq,
|
||||
require_neq, solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, source,
|
||||
system_program::System, zero_copy, AccountDeserialize, AccountSerialize, Accounts,
|
||||
AccountsClose, AccountsExit, AnchorDeserialize, AnchorSerialize, Id, InitSpace, Key, Owner,
|
||||
ProgramData, Result, Space, ToAccountInfo, ToAccountInfos, ToAccountMetas,
|
||||
AccountsClose, AccountsExit, AnchorDeserialize, AnchorSerialize, Id, InitSpace, Key,
|
||||
Lamports, Owner, ProgramData, Result, Space, ToAccountInfo, ToAccountInfos, ToAccountMetas,
|
||||
};
|
||||
#[cfg(feature = "event-cpi")]
|
||||
pub use super::{emit_cpi, event_cpi};
|
||||
|
|
|
@ -3,10 +3,11 @@ cluster = "localnet"
|
|||
wallet = "~/.config/solana/id.json"
|
||||
|
||||
[programs.localnet]
|
||||
misc = "3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh"
|
||||
misc_optional = "FNqz6pqLAwvMSds2FYjR4nKV3moVpPNtvkfGFrqLKrgG"
|
||||
idl_doc = "BqmKjZGVa8fqyWuojJzG16zaKSV1GjAisZToNuvEaz6m"
|
||||
init_if_needed = "BZoppwWi6jMnydnUBEJzotgEXHwLr3b3NramJgZtWeF2"
|
||||
lamports = "Lamports11111111111111111111111111111111111"
|
||||
misc = "3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh"
|
||||
misc_optional = "FNqz6pqLAwvMSds2FYjR4nKV3moVpPNtvkfGFrqLKrgG"
|
||||
|
||||
[workspace]
|
||||
exclude = ["programs/shared"]
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "lamports"
|
||||
version = "0.1.0"
|
||||
description = "Created with Anchor"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
|
||||
[features]
|
||||
no-entrypoint = []
|
||||
cpi = ["no-entrypoint"]
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = { path = "../../../../lang" }
|
|
@ -0,0 +1,2 @@
|
|||
[target.bpfel-unknown-unknown.dependencies.std]
|
||||
features = []
|
|
@ -0,0 +1,75 @@
|
|||
use anchor_lang::prelude::*;
|
||||
|
||||
declare_id!("Lamports11111111111111111111111111111111111");
|
||||
|
||||
#[program]
|
||||
pub mod lamports {
|
||||
use super::*;
|
||||
|
||||
pub fn test_lamports_trait(ctx: Context<TestLamportsTrait>, amount: u64) -> Result<()> {
|
||||
let pda = &ctx.accounts.pda;
|
||||
let signer = &ctx.accounts.signer;
|
||||
|
||||
// Transfer **to** PDA
|
||||
{
|
||||
// Get the balance of the PDA **before** the transfer to PDA
|
||||
let pda_balance_before = pda.get_lamports();
|
||||
|
||||
// Transfer to the PDA
|
||||
anchor_lang::system_program::transfer(
|
||||
CpiContext::new(
|
||||
ctx.accounts.system_program.to_account_info(),
|
||||
anchor_lang::system_program::Transfer {
|
||||
from: signer.to_account_info(),
|
||||
to: pda.to_account_info(),
|
||||
},
|
||||
),
|
||||
amount,
|
||||
)?;
|
||||
|
||||
// Get the balance of the PDA **after** the transfer to PDA
|
||||
let pda_balance_after = pda.get_lamports();
|
||||
|
||||
// Validate balance
|
||||
require_eq!(pda_balance_after, pda_balance_before + amount);
|
||||
}
|
||||
|
||||
// Transfer **from** PDA
|
||||
{
|
||||
// Get the balance of the PDA **before** the transfer from PDA
|
||||
let pda_balance_before = pda.get_lamports();
|
||||
|
||||
// Transfer from the PDA
|
||||
pda.sub_lamports(amount)?;
|
||||
signer.add_lamports(amount)?;
|
||||
|
||||
// Get the balance of the PDA **after** the transfer from PDA
|
||||
let pda_balance_after = pda.get_lamports();
|
||||
|
||||
// Validate balance
|
||||
require_eq!(pda_balance_after, pda_balance_before - amount);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct TestLamportsTrait<'info> {
|
||||
#[account(mut)]
|
||||
pub signer: Signer<'info>,
|
||||
|
||||
#[account(
|
||||
init,
|
||||
payer = signer,
|
||||
space = 8,
|
||||
seeds = [b"lamports"],
|
||||
bump
|
||||
)]
|
||||
pub pda: Account<'info, LamportsPda>,
|
||||
|
||||
pub system_program: Program<'info, System>,
|
||||
}
|
||||
|
||||
#[account]
|
||||
pub struct LamportsPda {}
|
|
@ -0,0 +1,2 @@
|
|||
[scripts]
|
||||
test = "yarn run ts-mocha -t 1000000 ./tests/lamports/*.ts"
|
|
@ -0,0 +1,23 @@
|
|||
import * as anchor from "@coral-xyz/anchor";
|
||||
|
||||
import { Lamports, IDL } from "../../target/types/lamports";
|
||||
|
||||
describe(IDL.name, () => {
|
||||
// Configure the client to use the local cluster
|
||||
anchor.setProvider(anchor.AnchorProvider.env());
|
||||
|
||||
const program = anchor.workspace.Lamports as anchor.Program<Lamports>;
|
||||
|
||||
it("Can use the Lamports trait", async () => {
|
||||
const signer = program.provider.publicKey!;
|
||||
const [pda] = anchor.web3.PublicKey.findProgramAddressSync(
|
||||
[Buffer.from("lamports")],
|
||||
program.programId
|
||||
);
|
||||
|
||||
await program.methods
|
||||
.testLamportsTrait(new anchor.BN(anchor.web3.LAMPORTS_PER_SOL))
|
||||
.accounts({ signer, pda })
|
||||
.rpc();
|
||||
});
|
||||
});
|
|
@ -1,11 +1,10 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"types": ["mocha", "chai"],
|
||||
"typeRoots": ["./node_modules/@types"],
|
||||
"lib": ["es2015"],
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
}
|
||||
"compilerOptions": {
|
||||
"types": ["mocha", "node"],
|
||||
"lib": ["ES6"],
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
# TODO: Remove when `cargo-test-sbf` works with stable Rust
|
||||
[toolchain]
|
||||
channel = "1.66.1-x86_64-unknown-linux-gnu"
|
Loading…
Reference in New Issue