Remove tests. When BPF SDK: /home/clarkeni/.local/share/solana/install/releases/1.10.29/solana-release/bin/sdk/bpf

cargo-build-bpf child: rustup toolchain list -v
cargo-build-bpf child: cargo +bpf build --target bpfel-unknown-unknown --release
cargo-build-bpf child: /home/clarkeni/.local/share/solana/install/releases/1.10.29/solana-release/bin/sdk/bpf/scripts/strip.sh /home/clarkeni/code/mango-v4/target/bpfel-unknown-unknown/release/mango_v4.so /home/clarkeni/code/mango-v4/target/deploy/mango_v4.so
cargo-build-bpf child: /home/clarkeni/.local/share/solana/install/releases/1.10.29/solana-release/bin/sdk/bpf/dependencies/bpf-tools/llvm/bin/llvm-readelf --dyn-symbols /home/clarkeni/code/mango-v4/target/deploy/mango_v4.so

To deploy this program:
  $ solana program deploy /home/clarkeni/code/mango-v4/target/deploy/mango_v4.so
The program address will default to this keypair (override with --program-id):
  /home/clarkeni/code/mango-v4/target/deploy/mango_v4-keypair.json is ran with anchor as a submodule it incorrectly tries to run all submodule tests as well
This commit is contained in:
Nicholas 2022-08-06 00:01:44 -07:00
parent b52f236146
commit 9e546f52d9
363 changed files with 0 additions and 23954 deletions

View File

@ -1,3 +0,0 @@
**/target/types/*.ts
cfo/deps/
auction-house/deps/

View File

@ -1,7 +0,0 @@
# Tests
No program here is guaranteed to be safe or secure in any way. In most cases, they are
simply just simple integration tests to illustrate some particular functionality
of the framework. If used, one should audit any programs used and take full
responsibility for the consequences that occur due to any outstanding bugs
or security vulnerabilities that exist.

View File

@ -1,16 +0,0 @@
[features]
seeds = false
[programs.localnet]
idl_commands_one = "2uA3amp95zsEHUpo8qnLMhcFAUsiKVEcKHXS1JetFjU5"
idl_commands_two = "DE4UbHnAcT6Kfh1fVTPRPwpiA3vipmQ4xR3gcLwX3wwS"
[registry]
url = "https://anchor.projectserum.com"
[provider]
cluster = "localnet"
wallet = "./keypairs/deployer-keypair.json"
[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

View File

@ -1,7 +0,0 @@
[profile.release]
overflow-checks = true
[workspace]
members = [
"programs/*"
]

View File

@ -1 +0,0 @@
[164,152,29,200,163,125,1,132,86,139,100,111,64,167,29,222,179,216,47,111,217,86,2,178,218,42,72,47,0,153,173,236,13,61,210,33,74,223,99,110,68,220,145,123,246,228,31,59,49,171,192,81,98,88,48,234,139,75,103,103,37,74,182,34]

View File

@ -1 +0,0 @@
[94,216,182,223,16,41,49,136,185,3,66,213,109,246,240,174,89,197,124,116,127,169,30,24,121,73,68,181,56,13,95,164,28,56,29,156,175,177,255,55,3,211,19,248,245,194,166,177,104,58,249,124,97,88,18,11,252,61,240,174,10,218,5,202]

View File

@ -1 +0,0 @@
[97,131,36,142,66,37,137,233,196,84,65,128,4,46,96,251,52,1,254,169,72,193,88,186,252,19,204,232,29,55,2,86,181,164,185,63,247,168,27,24,156,83,149,184,17,138,206,99,52,0,13,42,107,119,126,35,251,52,134,231,178,93,7,29]

View File

@ -1,12 +0,0 @@
// Migrations are an early feature. Currently, they're nothing more than this
// single deploy script that's invoked from the CLI, injecting a provider
// configured from the workspace's Anchor.toml.
const anchor = require("@project-serum/anchor");
module.exports = async function (provider) {
// Configure client to use the provider.
anchor.setProvider(provider);
// Add your deploy script here.
};

View File

@ -1,22 +0,0 @@
{
"name": "anchor-cli-idl",
"version": "0.25.0",
"license": "(MIT OR Apache-2.0)",
"homepage": "https://github.com/coral-xyz/anchor#readme",
"bugs": {
"url": "https://github.com/coral-xyz/anchor/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/coral-xyz/anchor.git"
},
"engines": {
"node": ">=11"
},
"scripts": {
"test": "./test.sh"
},
"dependencies": {
"mocha": "^9.1.3"
}
}

View File

@ -1,19 +0,0 @@
[package]
name = "idl-commands-one"
version = "0.1.0"
description = "Created with Anchor"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
name = "idl_commands_one"
[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []
[dependencies]
anchor-lang = { path = "../../../../lang" }

View File

@ -1,2 +0,0 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -1,15 +0,0 @@
use anchor_lang::prelude::*;
declare_id!("2uA3amp95zsEHUpo8qnLMhcFAUsiKVEcKHXS1JetFjU5");
#[program]
pub mod idl_commands_one {
use super::*;
pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize {}

View File

@ -1,19 +0,0 @@
[package]
name = "idl-commands-two"
version = "0.1.0"
description = "Created with Anchor"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
name = "idl_commands_two"
[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []
[dependencies]
anchor-lang = { path = "../../../../lang" }

View File

@ -1,2 +0,0 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -1,15 +0,0 @@
use anchor_lang::prelude::*;
declare_id!("DE4UbHnAcT6Kfh1fVTPRPwpiA3vipmQ4xR3gcLwX3wwS");
#[program]
pub mod idl_commands_two {
use super::*;
pub fn uninitialize(_ctx: Context<Initialize>) -> Result<()> {
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize {}

View File

@ -1,26 +0,0 @@
#!/bin/bash
# Write a keypair for program deploy
mkdir -p target/deploy
cp keypairs/idl_commands_one-keypair.json target/deploy
echo "Building programs"
anchor build
echo "Starting local validator for test"
solana-test-validator --reset \
-q \
--mint tgyXxAhCkpgtKCEi4W6xWJSzqwVGs3uk2RodbZP2J49 \
--bpf-program 2uA3amp95zsEHUpo8qnLMhcFAUsiKVEcKHXS1JetFjU5 target/deploy/idl_commands_one.so \
--bpf-program DE4UbHnAcT6Kfh1fVTPRPwpiA3vipmQ4xR3gcLwX3wwS target/deploy/idl_commands_one.so \
&
sleep 10
echo "Running tests"
anchor test --skip-deploy --skip-local-validator
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT

View File

@ -1,65 +0,0 @@
import * as anchor from "@project-serum/anchor";
import { Program } from "@project-serum/anchor";
import { IdlCommandsOne } from "../target/types/idl_commands_one";
import { IdlCommandsTwo } from "../target/types/idl_commands_two";
import { assert } from "chai";
import { execSync } from "child_process";
describe("Test CLI IDL commands", () => {
// Configure the client to use the local cluster.
const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
const programOne = anchor.workspace.IdlCommandsOne as Program<IdlCommandsOne>;
const programTwo = anchor.workspace.IdlCommandsTwo as Program<IdlCommandsTwo>;
it("Can initialize IDL account", async () => {
execSync(
`anchor idl init --filepath target/idl/idl_commands_one.json ${programOne.programId}`,
{ stdio: "inherit" }
);
});
it("Can fetch an IDL using the TypeScript client", async () => {
const idl = await anchor.Program.fetchIdl(programOne.programId, provider);
assert.deepEqual(idl, programOne.idl);
});
it("Can fetch an IDL via the CLI", async () => {
const idl = execSync(`anchor idl fetch ${programOne.programId}`).toString();
assert.deepEqual(JSON.parse(idl), programOne.idl);
});
it("Can write a new IDL using the upgrade command", async () => {
// Upgrade the IDL of program one to the IDL of program two to test upgrade
execSync(
`anchor idl upgrade --filepath target/idl/idl_commands_two.json ${programOne.programId}`,
{ stdio: "inherit" }
);
const idl = await anchor.Program.fetchIdl(programOne.programId, provider);
assert.deepEqual(idl, programTwo.idl);
});
it("Can write a new IDL using write-buffer and set-buffer", async () => {
// "Upgrade" back to program one via write-buffer set-buffer
let buffer = execSync(
`anchor idl write-buffer --filepath target/idl/idl_commands_one.json ${programOne.programId}`
).toString();
buffer = buffer.replace("Idl buffer created: ", "").trim();
execSync(
`anchor idl set-buffer --buffer ${buffer} ${programOne.programId}`,
{ stdio: "inherit" }
);
const idl = await anchor.Program.fetchIdl(programOne.programId, provider);
assert.deepEqual(idl, programOne.idl);
});
it("Can fetch an IDL authority via the CLI", async () => {
const authority = execSync(`anchor idl authority ${programOne.programId}`)
.toString()
.trim();
assert.equal(authority, provider.wallet.publicKey.toString());
});
});

View File

@ -1,10 +0,0 @@
{
"compilerOptions": {
"types": ["mocha", "chai"],
"typeRoots": ["./node_modules/@types"],
"lib": ["es2015"],
"module": "commonjs",
"target": "es6",
"esModuleInterop": true
}
}

@ -1 +0,0 @@
Subproject commit 9b0c81e5972f2466d47c20f705fc5fd1ca16476c

View File

@ -1 +0,0 @@
yarn.lock

View File

@ -1,12 +0,0 @@
[programs.localnet]
bpf_upgradeable_state = "Cum9tTyj5HwcEiAmhgaS7Bbj4UczCwsucrCkxRECzM4e"
[registry]
url = "https://anchor.projectserum.com"
[provider]
cluster = "localnet"
wallet = "~/.config/solana/id.json"
[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

View File

@ -1,4 +0,0 @@
[workspace]
members = [
"programs/*"
]

View File

@ -1 +0,0 @@
[114,99,192,17,48,208,90,184,231,46,220,91,47,115,132,253,218,163,228,101,8,121,220,138,41,140,176,127,254,91,51,28,176,244,174,182,223,57,57,125,117,201,31,213,9,39,207,212,100,173,88,252,61,235,89,156,53,86,4,90,16,251,191,219]

View File

@ -1,12 +0,0 @@
// Migrations are an early feature. Currently, they're nothing more than this
// single deploy script that's invoked from the CLI, injecting a provider
// configured from the workspace's Anchor.toml.
const anchor = require("@project-serum/anchor");
module.exports = async function (provider) {
// Configure client to use the provider.
anchor.setProvider(provider);
// Add your deploy script here.
};

View File

@ -1,16 +0,0 @@
{
"name": "bpf-upgradeable-state",
"version": "0.24.0",
"license": "(MIT OR Apache-2.0)",
"homepage": "https://github.com/coral-xyz/anchor#readme",
"bugs": {
"url": "https://github.com/coral-xyz/anchor/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/coral-xyz/anchor.git"
},
"engines": {
"node": ">=11"
}
}

View File

@ -1 +0,0 @@
[86,234,116,86,82,140,116,250,254,32,75,217,35,39,9,238,39,98,242,254,25,216,201,66,1,239,93,12,81,19,34,108,219,67,158,98,245,234,81,126,228,157,205,206,130,5,14,54,1,21,88,246,128,124,240,93,157,49,102,19,253,19,205,178]

View File

@ -1,19 +0,0 @@
[package]
name = "bpf-upgradeable-state"
version = "0.1.0"
description = "Created with Anchor"
rust-version = "1.56"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
name = "bpf_upgradeable_state"
[features]
no-entrypoint = []
no-idl = []
cpi = ["no-entrypoint"]
default = []
[dependencies]
anchor-lang = { path = "../../../../lang" }

View File

@ -1,2 +0,0 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -1,80 +0,0 @@
use anchor_lang::prelude::*;
declare_id!("Cum9tTyj5HwcEiAmhgaS7Bbj4UczCwsucrCkxRECzM4e");
#[program]
pub mod bpf_upgradeable_state {
use super::*;
pub fn set_admin_settings(ctx: Context<SetAdminSettings>, admin_data: u64) -> Result<()> {
match *ctx.accounts.program {
UpgradeableLoaderState::Program {
programdata_address,
} => {
if programdata_address != ctx.accounts.program_data.key() {
return err!(CustomError::InvalidProgramDataAddress);
}
}
_ => {
return err!(CustomError::AccountNotProgram);
}
};
ctx.accounts.settings.admin_data = admin_data;
Ok(())
}
pub fn set_admin_settings_use_program_state(
ctx: Context<SetAdminSettingsUseProgramState>,
admin_data: u64,
) -> Result<()> {
ctx.accounts.settings.admin_data = admin_data;
Ok(())
}
}
#[account]
pub struct Settings {
admin_data: u64,
}
impl Settings {
pub const LEN: usize = 8;
}
#[error_code]
pub enum CustomError {
InvalidProgramDataAddress,
AccountNotProgram,
AccountNotBpfUpgradableProgram,
}
#[derive(Accounts)]
pub struct SetAdminSettings<'info> {
// In a real program, this should be a PDA,
// so the authority cannot create multiple settings accounts.
// Not done here for easier testing
#[account(init, payer = authority, space = Settings::LEN + 8)]
pub settings: Account<'info, Settings>,
#[account(mut)]
pub authority: Signer<'info>,
#[account(address = crate::ID)]
pub program: Account<'info, UpgradeableLoaderState>,
#[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))]
pub program_data: Account<'info, ProgramData>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct SetAdminSettingsUseProgramState<'info> {
// In a real program, this should be a PDA,
// so the authority cannot create multiple settings accounts.
// Not done here for easier testing
#[account(init, payer = authority, space = Settings::LEN + 8)]
pub settings: Account<'info, Settings>,
#[account(mut)]
pub authority: Signer<'info>,
#[account(constraint = program.programdata_address()? == Some(program_data.key()))]
pub program: Program<'info, crate::program::BpfUpgradeableState>,
#[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))]
pub program_data: Account<'info, ProgramData>,
pub system_program: Program<'info, System>,
}

View File

@ -1,203 +0,0 @@
import * as anchor from "@project-serum/anchor";
import { AnchorError, Program } from "@project-serum/anchor";
import { findProgramAddressSync } from "@project-serum/anchor/dist/cjs/utils/pubkey";
import { PublicKey } from "@solana/web3.js";
import { assert } from "chai";
import { BpfUpgradeableState } from "../target/types/bpf_upgradeable_state";
describe("bpf_upgradeable_state", () => {
const provider = anchor.AnchorProvider.env();
// Configure the client to use the local cluster.
anchor.setProvider(provider);
const program = anchor.workspace
.BpfUpgradeableState as Program<BpfUpgradeableState>;
const programDataAddress = findProgramAddressSync(
[program.programId.toBytes()],
new anchor.web3.PublicKey("BPFLoaderUpgradeab1e11111111111111111111111")
)[0];
it("Reads ProgramData and sets field", async () => {
const settings = anchor.web3.Keypair.generate();
const tx = await program.rpc.setAdminSettings(new anchor.BN(500), {
accounts: {
authority: provider.wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
programData: programDataAddress,
program: program.programId,
settings: settings.publicKey,
},
signers: [settings],
});
assert.strictEqual(
(
await program.account.settings.fetch(settings.publicKey)
).adminData.toNumber(),
500
);
});
it("Reads ProgramData and sets field, uses program state", async () => {
const settings = anchor.web3.Keypair.generate();
const tx = await program.rpc.setAdminSettingsUseProgramState(
new anchor.BN(500),
{
accounts: {
authority: provider.wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
programData: programDataAddress,
program: program.programId,
settings: settings.publicKey,
},
signers: [settings],
}
);
assert.strictEqual(
(
await program.account.settings.fetch(settings.publicKey)
).adminData.toNumber(),
500
);
});
it("Validates constraint on ProgramData", async () => {
const settings = anchor.web3.Keypair.generate();
try {
const authority = anchor.web3.Keypair.generate();
await provider.connection.confirmTransaction(
await provider.connection.requestAirdrop(
authority.publicKey,
10000000000
),
"confirmed"
);
await program.rpc.setAdminSettings(new anchor.BN(500), {
accounts: {
authority: authority.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
programData: programDataAddress,
settings: settings.publicKey,
program: program.programId,
},
signers: [settings, authority],
});
assert.ok(false);
} catch (_err) {
assert.isTrue(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.strictEqual(err.error.errorCode.number, 2003);
assert.strictEqual(
err.error.errorMessage,
"A raw constraint was violated"
);
}
});
it("Validates that account is ProgramData", async () => {
const settings = anchor.web3.Keypair.generate();
try {
await program.rpc.setAdminSettings(new anchor.BN(500), {
accounts: {
authority: provider.wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
programData: program.programId,
settings: settings.publicKey,
program: program.programId,
},
signers: [settings],
});
assert.ok(false);
} catch (_err) {
assert.isTrue(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.strictEqual(err.error.errorCode.number, 3013);
assert.strictEqual(
err.error.errorMessage,
"The given account is not a program data account"
);
}
});
it("Validates that account is owned by the upgradeable bpf loader", async () => {
const settings = anchor.web3.Keypair.generate();
try {
await program.rpc.setAdminSettings(new anchor.BN(500), {
accounts: {
authority: provider.wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
programData: provider.wallet.publicKey,
settings: settings.publicKey,
program: program.programId,
},
signers: [settings],
});
assert.ok(false);
} catch (_err) {
assert.isTrue(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.strictEqual(err.error.errorCode.number, 3007);
assert.strictEqual(
err.error.errorMessage,
"The given account is owned by a different program than expected"
);
}
});
it("Deserializes UpgradableLoaderState and validates that programData is the expected account", async () => {
const secondProgramAddress = new PublicKey(
"Fkv67TwmbakfZw2PoW57wYPbqNexAH6vuxpyT8vmrc3B"
);
const secondProgramProgramDataAddress = findProgramAddressSync(
[secondProgramAddress.toBytes()],
new anchor.web3.PublicKey("BPFLoaderUpgradeab1e11111111111111111111111")
)[0];
const settings = anchor.web3.Keypair.generate();
try {
await program.rpc.setAdminSettings(new anchor.BN(500), {
accounts: {
authority: provider.wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
programData: secondProgramProgramDataAddress,
settings: settings.publicKey,
program: program.programId,
},
signers: [settings],
});
assert.ok(false);
} catch (_err) {
assert.isTrue(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.strictEqual(err.error.errorCode.number, 6000);
}
});
it("Deserializes Program and validates that programData is the expected account", async () => {
const secondProgramAddress = new PublicKey(
"Fkv67TwmbakfZw2PoW57wYPbqNexAH6vuxpyT8vmrc3B"
);
const secondProgramProgramDataAddress = findProgramAddressSync(
[secondProgramAddress.toBytes()],
new anchor.web3.PublicKey("BPFLoaderUpgradeab1e11111111111111111111111")
)[0];
const settings = anchor.web3.Keypair.generate();
try {
await program.rpc.setAdminSettingsUseProgramState(new anchor.BN(500), {
accounts: {
authority: provider.wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
programData: secondProgramProgramDataAddress,
settings: settings.publicKey,
program: program.programId,
},
signers: [settings],
});
assert.ok(false);
} catch (_err) {
assert.isTrue(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.strictEqual(err.error.errorCode.number, 2003);
}
});
});

View File

@ -1,11 +0,0 @@
{
"compilerOptions": {
"types": ["mocha", "chai"],
"typeRoots": ["./node_modules/@types"],
"lib": ["es2015"],
"module": "commonjs",
"target": "es6",
"esModuleInterop": true,
"skipLibCheck": true
}
}

View File

@ -1,11 +0,0 @@
[provider]
cluster = "localnet"
wallet = "~/.config/solana/id.json"
[programs.localnet]
cashiers_check = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
[scripts]
test = "yarn run mocha -t 1000000 tests/"
[features]

View File

@ -1,4 +0,0 @@
[workspace]
members = [
"programs/*"
]

View File

@ -1,12 +0,0 @@
// Migrations are an early feature. Currently, they're nothing more than this
// single deploy script that's invoked from the CLI, injecting a provider
// configured from the workspace's Anchor.toml.
const anchor = require("@project-serum/anchor");
module.exports = async function (provider) {
// Configure client to use the provider.
anchor.setProvider(provider);
// Add your deploy script here.
};

View File

@ -1,19 +0,0 @@
{
"name": "cashiers-check",
"version": "0.25.0",
"license": "(MIT OR Apache-2.0)",
"homepage": "https://github.com/coral-xyz/anchor#readme",
"bugs": {
"url": "https://github.com/coral-xyz/anchor/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/coral-xyz/anchor.git"
},
"engines": {
"node": ">=11"
},
"scripts": {
"test": "anchor test"
}
}

View File

@ -1,20 +0,0 @@
[package]
name = "cashiers-check"
version = "0.1.0"
description = "Created with Anchor"
rust-version = "1.56"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
name = "cashiers_check"
[features]
no-entrypoint = []
no-idl = []
cpi = ["no-entrypoint"]
default = []
[dependencies]
anchor-lang = { path = "../../../../lang" }
anchor-spl = { path = "../../../../spl" }

View File

@ -1,2 +0,0 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -1,180 +0,0 @@
//! A cashiers check example. The funds are immediately withdrawn from a user's
//! account and sent to a program controlled `Check` account, where the funds
//! reside until they are "cashed" by the intended recipient. The creator of
//! the check can cancel the check at any time to get back the funds.
use anchor_lang::prelude::*;
use anchor_spl::token::{self, TokenAccount, Transfer};
use std::convert::Into;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
pub mod cashiers_check {
use super::*;
#[access_control(CreateCheck::accounts(&ctx, nonce))]
pub fn create_check(
ctx: Context<CreateCheck>,
amount: u64,
memo: Option<String>,
nonce: u8,
) -> Result<()> {
// Transfer funds to the check.
let cpi_accounts = Transfer {
from: ctx.accounts.from.to_account_info().clone(),
to: ctx.accounts.vault.to_account_info().clone(),
authority: ctx.accounts.owner.clone(),
};
let cpi_program = ctx.accounts.token_program.clone();
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
token::transfer(cpi_ctx, amount)?;
// Print the check.
let check = &mut ctx.accounts.check;
check.amount = amount;
check.from = *ctx.accounts.from.to_account_info().key;
check.to = *ctx.accounts.to.to_account_info().key;
check.vault = *ctx.accounts.vault.to_account_info().key;
check.nonce = nonce;
check.memo = memo;
Ok(())
}
#[access_control(not_burned(&ctx.accounts.check))]
pub fn cash_check(ctx: Context<CashCheck>) -> Result<()> {
let seeds = &[
ctx.accounts.check.to_account_info().key.as_ref(),
&[ctx.accounts.check.nonce],
];
let signer = &[&seeds[..]];
let cpi_accounts = Transfer {
from: ctx.accounts.vault.to_account_info().clone(),
to: ctx.accounts.to.to_account_info().clone(),
authority: ctx.accounts.check_signer.clone(),
};
let cpi_program = ctx.accounts.token_program.clone();
let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer);
token::transfer(cpi_ctx, ctx.accounts.check.amount)?;
// Burn the check for one time use.
ctx.accounts.check.burned = true;
Ok(())
}
#[access_control(not_burned(&ctx.accounts.check))]
pub fn cancel_check(ctx: Context<CancelCheck>) -> Result<()> {
let seeds = &[
ctx.accounts.check.to_account_info().key.as_ref(),
&[ctx.accounts.check.nonce],
];
let signer = &[&seeds[..]];
let cpi_accounts = Transfer {
from: ctx.accounts.vault.to_account_info().clone(),
to: ctx.accounts.from.to_account_info().clone(),
authority: ctx.accounts.check_signer.clone(),
};
let cpi_program = ctx.accounts.token_program.clone();
let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer);
token::transfer(cpi_ctx, ctx.accounts.check.amount)?;
ctx.accounts.check.burned = true;
Ok(())
}
}
#[derive(Accounts)]
pub struct CreateCheck<'info> {
// Check being created.
#[account(zero)]
check: Account<'info, Check>,
// Check's token vault.
#[account(mut, constraint = &vault.owner == check_signer.key)]
vault: Account<'info, TokenAccount>,
// Program derived address for the check.
check_signer: AccountInfo<'info>,
// Token account the check is made from.
#[account(mut, has_one = owner)]
from: Account<'info, TokenAccount>,
// Token account the check is made to.
#[account(constraint = from.mint == to.mint)]
to: Account<'info, TokenAccount>,
// Owner of the `from` token account.
owner: AccountInfo<'info>,
token_program: AccountInfo<'info>,
}
impl<'info> CreateCheck<'info> {
pub fn accounts(ctx: &Context<CreateCheck>, nonce: u8) -> Result<()> {
let signer = Pubkey::create_program_address(
&[ctx.accounts.check.to_account_info().key.as_ref(), &[nonce]],
ctx.program_id,
)
.map_err(|_| error!(ErrorCode::InvalidCheckNonce))?;
if &signer != ctx.accounts.check_signer.to_account_info().key {
return err!(ErrorCode::InvalidCheckSigner);
}
Ok(())
}
}
#[derive(Accounts)]
pub struct CashCheck<'info> {
#[account(mut, has_one = vault, has_one = to)]
check: Account<'info, Check>,
#[account(mut)]
vault: AccountInfo<'info>,
#[account(
seeds = [check.to_account_info().key.as_ref()],
bump = check.nonce,
)]
check_signer: AccountInfo<'info>,
#[account(mut, has_one = owner)]
to: Account<'info, TokenAccount>,
owner: Signer<'info>,
token_program: AccountInfo<'info>,
}
#[derive(Accounts)]
pub struct CancelCheck<'info> {
#[account(mut, has_one = vault, has_one = from)]
check: Account<'info, Check>,
#[account(mut)]
vault: AccountInfo<'info>,
#[account(
seeds = [check.to_account_info().key.as_ref()],
bump = check.nonce,
)]
check_signer: AccountInfo<'info>,
#[account(mut, has_one = owner)]
from: Account<'info, TokenAccount>,
owner: Signer<'info>,
token_program: AccountInfo<'info>,
}
#[account]
pub struct Check {
from: Pubkey,
to: Pubkey,
amount: u64,
memo: Option<String>,
vault: Pubkey,
nonce: u8,
burned: bool,
}
#[error_code]
pub enum ErrorCode {
#[msg("The given nonce does not create a valid program derived address.")]
InvalidCheckNonce,
#[msg("The derived check signer does not match that which was given.")]
InvalidCheckSigner,
#[msg("The given check has already been burned.")]
AlreadyBurned,
}
fn not_burned(check: &Check) -> Result<()> {
if check.burned {
return err!(ErrorCode::AlreadyBurned);
}
Ok(())
}

View File

@ -1,113 +0,0 @@
const anchor = require("@project-serum/anchor");
const serumCmn = require("@project-serum/common");
const { assert } = require("chai");
const { TOKEN_PROGRAM_ID } = require("@solana/spl-token");
describe("cashiers-check", () => {
// Configure the client to use the local cluster.
const provider = anchor.AnchorProvider.env();
// hack so we don't have to update serum-common library
// to the new AnchorProvider class and Provider interface
provider.send = provider.sendAndConfirm;
anchor.setProvider(provider);
const program = anchor.workspace.CashiersCheck;
let mint = null;
let god = null;
let receiver = null;
it("Sets up initial test state", async () => {
const [_mint, _god] = await serumCmn.createMintAndVault(
program.provider,
new anchor.BN(1000000)
);
mint = _mint;
god = _god;
receiver = await serumCmn.createTokenAccount(
program.provider,
mint,
program.provider.wallet.publicKey
);
});
const check = anchor.web3.Keypair.generate();
const vault = anchor.web3.Keypair.generate();
let checkSigner = null;
it("Creates a check!", async () => {
let [_checkSigner, nonce] = await anchor.web3.PublicKey.findProgramAddress(
[check.publicKey.toBuffer()],
program.programId
);
checkSigner = _checkSigner;
await program.rpc.createCheck(new anchor.BN(100), "Hello world", nonce, {
accounts: {
check: check.publicKey,
vault: vault.publicKey,
checkSigner,
from: god,
to: receiver,
owner: program.provider.wallet.publicKey,
tokenProgram: TOKEN_PROGRAM_ID,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
},
signers: [check, vault],
instructions: [
await program.account.check.createInstruction(check, 300),
...(await serumCmn.createTokenAccountInstrs(
program.provider,
vault.publicKey,
mint,
checkSigner
)),
],
});
const checkAccount = await program.account.check.fetch(check.publicKey);
assert.isTrue(checkAccount.from.equals(god));
assert.isTrue(checkAccount.to.equals(receiver));
assert.isTrue(checkAccount.amount.eq(new anchor.BN(100)));
assert.strictEqual(checkAccount.memo, "Hello world");
assert.isTrue(checkAccount.vault.equals(vault.publicKey));
assert.strictEqual(checkAccount.nonce, nonce);
assert.isFalse(checkAccount.burned);
let vaultAccount = await serumCmn.getTokenAccount(
program.provider,
checkAccount.vault
);
assert.isTrue(vaultAccount.amount.eq(new anchor.BN(100)));
});
it("Cashes a check", async () => {
await program.rpc.cashCheck({
accounts: {
check: check.publicKey,
vault: vault.publicKey,
checkSigner: checkSigner,
to: receiver,
owner: program.provider.wallet.publicKey,
tokenProgram: TOKEN_PROGRAM_ID,
},
});
const checkAccount = await program.account.check.fetch(check.publicKey);
assert.isTrue(checkAccount.burned);
let vaultAccount = await serumCmn.getTokenAccount(
program.provider,
checkAccount.vault
);
assert.isTrue(vaultAccount.amount.eq(new anchor.BN(0)));
let receiverAccount = await serumCmn.getTokenAccount(
program.provider,
receiver
);
assert.isTrue(receiverAccount.amount.eq(new anchor.BN(100)));
});
});

View File

@ -1,45 +0,0 @@
[provider]
cluster = "localnet"
wallet = "~/.config/solana/id.json"
[programs.localnet]
cfo = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
registry = { address = "GrAkKfEpTKQuVHG2Y97Y2FF4i7y7Q5AHLK94JBy7Y5yv", idl = "./deps/stake/target/idl/registry.json" }
lockup = { address = "6ebQNeTPZ1j7k3TtkCCtEPRvG7GQsucQrZ7sSEDQi9Ks", idl = "./deps/stake/target/idl/lockup.json" }
[scripts]
#
# Testing.
#
test = "yarn run mocha -t 1000000 tests/"
test-with-build = "anchor run build && anchor test --skip-build --skip-lint"
#
# Build the program and all CPI dependencies.
#
build = "anchor run build-deps && anchor build --skip-lint"
build-deps = "anchor run build-dex && anchor run build-swap && anchor run build-stake"
build-dex = "pushd deps/serum-dex/dex/ && cargo build-bpf && popd"
build-swap = "cd deps/swap && pwd && anchor build --skip-lint && cd ../../"
build-stake = "pushd deps/stake && anchor build --skip-lint && popd"
#
# Runs a localnet with all the programs deployed.
#
localnet = "./scripts/localnet.sh"
[[test.genesis]]
address = "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"
program = "./deps/serum-dex/dex/target/deploy/serum_dex.so"
[[test.genesis]]
address = "22Y43yTVxuUkoRKdm9thyRhQ3SdgQS7c7kB6UNCiaczD"
program = "./deps/swap/target/deploy/swap.so"
[[test.genesis]]
address = "GrAkKfEpTKQuVHG2Y97Y2FF4i7y7Q5AHLK94JBy7Y5yv"
program = "./deps/stake/target/deploy/registry.so"
[[test.genesis]]
address = "6ebQNeTPZ1j7k3TtkCCtEPRvG7GQsucQrZ7sSEDQi9Ks"
program = "./deps/stake/target/deploy/lockup.so"
[features]

View File

@ -1,9 +0,0 @@
[workspace]
members = [
"programs/*"
]
exclude = [
"deps/serum-dex",
"deps/stake",
"deps/swap"
]

@ -1 +0,0 @@
Subproject commit ed9d54a717bec01de2924f6e6ca465f942b072aa

@ -1 +0,0 @@
Subproject commit fd78344ab5f34c36a91bdaf8b9edf2fbd8a93510

@ -1 +0,0 @@
Subproject commit 9c6bd407eb96ac77f4aef0d4387ca5b122f1c20c

View File

@ -1,12 +0,0 @@
// Migrations are an early feature. Currently, they're nothing more than this
// single deploy script that's invoked from the CLI, injecting a provider
// configured from the workspace's Anchor.toml.
const anchor = require("@project-serum/anchor");
module.exports = async function (provider) {
// Configure client to use the provider.
anchor.setProvider(provider);
// Add your deploy script here.
};

View File

@ -1,19 +0,0 @@
{
"name": "cfo",
"version": "0.25.0",
"license": "(MIT OR Apache-2.0)",
"homepage": "https://github.com/coral-xyz/anchor#readme",
"bugs": {
"url": "https://github.com/coral-xyz/anchor/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/coral-xyz/anchor.git"
},
"engines": {
"node": ">=11"
},
"scripts": {
"test": "anchor run test-with-build"
}
}

View File

@ -1,26 +0,0 @@
[package]
name = "cfo"
version = "0.1.0"
description = "Created with Anchor"
rust-version = "1.56"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
name = "cfo"
[features]
no-entrypoint = []
no-idl = []
cpi = ["no-entrypoint"]
default = ["test"]
test = []
[dependencies]
anchor-lang = { path = "../../../../lang" }
anchor-spl = { path = "../../../../spl" }
spl-token = { version = "~3.3.0", features = ["no-entrypoint"] }
swap = { path = "../../deps/swap/programs/swap", features = ["cpi"] }
serum_dex = { path = "../../deps/serum-dex/dex", features = ["no-entrypoint"] }
registry = { path = "../../deps/stake/programs/registry", features = ["cpi"] }
lockup = { path = "../../deps/stake/programs/lockup", features = ["cpi"] }

View File

@ -1,2 +0,0 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -1,995 +0,0 @@
// WIP. This program has been checkpointed and is not production ready.
use anchor_lang::prelude::*;
use anchor_lang::solana_program::sysvar::instructions as tx_instructions;
use anchor_spl::dex::{self, Dex};
use anchor_spl::token::{self, Mint, Token, TokenAccount};
use lockup::program::Lockup;
use registry::program::Registry;
use registry::{Registrar, RewardVendorKind};
use serum_dex::state::OpenOrders;
use std::convert::TryInto;
use std::mem::size_of;
use swap::program::Swap;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
/// CFO is the program representing the Serum chief financial officer. It is
/// the program responsible for collecting and distributing fees from the Serum
/// DEX.
#[program]
pub mod cfo {
use super::*;
/// Creates a financial officer account associated with a DEX program ID.
#[access_control(is_distribution_valid(&d))]
pub fn create_officer(
ctx: Context<CreateOfficer>,
bumps: OfficerBumps,
d: Distribution,
registrar: Pubkey,
msrm_registrar: Pubkey,
) -> Result<()> {
let officer = &mut ctx.accounts.officer;
officer.authority = *ctx.accounts.authority.key;
officer.swap_program = *ctx.accounts.swap_program.key;
officer.dex_program = ctx.accounts.dex_program.key();
officer.distribution = d;
officer.registrar = registrar;
officer.msrm_registrar = msrm_registrar;
officer.stake = *ctx.accounts.stake.to_account_info().key;
officer.treasury = *ctx.accounts.treasury.to_account_info().key;
officer.srm_vault = *ctx.accounts.srm_vault.to_account_info().key;
officer.bumps = bumps;
emit!(OfficerDidCreate {
pubkey: *officer.to_account_info().key,
});
Ok(())
}
/// Creates a market authorization token.
pub fn authorize_market(ctx: Context<AuthorizeMarket>, bump: u8) -> Result<()> {
ctx.accounts.market_auth.bump = bump;
Ok(())
}
/// Revokes a market authorization token.
pub fn revoke_market(_ctx: Context<RevokeMarket>) -> Result<()> {
Ok(())
}
/// Creates a deterministic token account owned by the CFO.
/// This should be used when a new mint is used for collecting fees.
/// Can only be called once per token CFO and token mint.
pub fn create_officer_token(_ctx: Context<CreateOfficerToken>, _bump: u8) -> Result<()> {
Ok(())
}
/// Creates an open orders account for the given market.
pub fn create_officer_open_orders(
ctx: Context<CreateOfficerOpenOrders>,
_bump: u8,
) -> Result<()> {
let seeds = [
ctx.accounts.dex_program.key.as_ref(),
&[ctx.accounts.officer.bumps.bump],
];
let cpi_ctx = CpiContext::from(&*ctx.accounts);
dex::init_open_orders(cpi_ctx.with_signer(&[&seeds])).map_err(Into::into)
}
/// Updates the cfo's fee distribution.
#[access_control(is_distribution_valid(&d))]
pub fn set_distribution(ctx: Context<SetDistribution>, d: Distribution) -> Result<()> {
ctx.accounts.officer.distribution = d.clone();
emit!(DistributionDidChange { distribution: d });
Ok(())
}
/// Transfers fees from the dex to the CFO.
pub fn sweep_fees<'info>(ctx: Context<'_, '_, '_, 'info, SweepFees<'info>>) -> Result<()> {
let cpi_ctx = CpiContext::from(&*ctx.accounts);
let seeds = [
ctx.accounts.dex.dex_program.key.as_ref(),
&[ctx.accounts.officer.bumps.bump],
];
dex::sweep_fees(cpi_ctx.with_signer(&[&seeds])).map_err(Into::into)
}
/// Convert the CFO's entire non-SRM token balance into USDC.
/// Assumes USDC is the quote currency.
#[access_control(is_not_trading(&ctx.accounts.instructions))]
pub fn swap_to_usdc<'info>(
ctx: Context<'_, '_, '_, 'info, SwapToUsdc<'info>>,
min_exchange_rate: ExchangeRate,
) -> Result<()> {
let seeds = [
ctx.accounts.dex_program.key.as_ref(),
&[ctx.accounts.officer.bumps.bump],
];
let cpi_ctx = CpiContext::from(&*ctx.accounts);
swap::cpi::swap(
cpi_ctx.with_signer(&[&seeds]),
swap::Side::Ask,
ctx.accounts.from_vault.amount,
min_exchange_rate.into(),
)
.map_err(Into::into)
}
/// Convert the CFO's entire token balance into SRM.
/// Assumes SRM is the base currency.
#[access_control(is_not_trading(&ctx.accounts.instructions))]
pub fn swap_to_srm<'info>(
ctx: Context<'_, '_, '_, 'info, SwapToSrm<'info>>,
min_exchange_rate: ExchangeRate,
) -> Result<()> {
let seeds = [
ctx.accounts.dex_program.key.as_ref(),
&[ctx.accounts.officer.bumps.bump],
];
let cpi_ctx = CpiContext::from(&*ctx.accounts);
swap::cpi::swap(
cpi_ctx.with_signer(&[&seeds]),
swap::Side::Bid,
ctx.accounts.usdc_vault.amount,
min_exchange_rate.into(),
)
.map_err(Into::into)
}
/// Distributes srm tokens to the various categories. Before calling this,
/// one must convert the fees into SRM via the swap APIs.
#[access_control(is_distribution_ready(&ctx.accounts))]
pub fn distribute<'info>(ctx: Context<'_, '_, '_, 'info, Distribute<'info>>) -> Result<()> {
let total_fees = ctx.accounts.srm_vault.amount;
let seeds = [
ctx.accounts.dex_program.key.as_ref(),
&[ctx.accounts.officer.bumps.bump],
];
// Burn.
let burn_amount: u64 = u128::from(total_fees)
.checked_mul(ctx.accounts.officer.distribution.burn.into())
.unwrap()
.checked_div(100)
.unwrap()
.try_into()
.map_err(|_| error!(ErrorCode::U128CannotConvert))?;
token::burn(ctx.accounts.into_burn().with_signer(&[&seeds]), burn_amount)?;
// Stake.
let stake_amount: u64 = u128::from(total_fees)
.checked_mul(ctx.accounts.officer.distribution.stake.into())
.unwrap()
.checked_div(100)
.unwrap()
.try_into()
.map_err(|_| error!(ErrorCode::U128CannotConvert))?;
token::transfer(
ctx.accounts.into_stake_transfer().with_signer(&[&seeds]),
stake_amount,
)?;
// Treasury.
let treasury_amount: u64 = u128::from(total_fees)
.checked_mul(ctx.accounts.officer.distribution.treasury.into())
.unwrap()
.checked_div(100)
.unwrap()
.try_into()
.map_err(|_| error!(ErrorCode::U128CannotConvert))?;
token::transfer(
ctx.accounts.into_treasury_transfer().with_signer(&[&seeds]),
treasury_amount,
)?;
Ok(())
}
#[access_control(is_stake_reward_ready(&ctx.accounts))]
pub fn drop_stake_reward<'info>(
ctx: Context<'_, '_, '_, 'info, DropStakeReward<'info>>,
) -> Result<()> {
// Common reward parameters.
let expiry_ts = 1853942400; // 9/30/2028.
let expiry_receiver = *ctx.accounts.officer.to_account_info().key;
let locked_kind = {
let start_ts = 1633017600; // 9/30.25.0.
let end_ts = 1822320000; // 9/30/2027.
let period_count = 2191;
RewardVendorKind::Locked {
start_ts,
end_ts,
period_count,
}
};
let seeds = [
ctx.accounts.dex_program.key.as_ref(),
&[ctx.accounts.officer.bumps.bump],
];
// Total amount staked denominated in SRM (i.e. MSRM is converted to
// SRM)
let total_pool_value = u128::from(ctx.accounts.srm.pool_mint.supply)
.checked_mul(500)
.unwrap()
.checked_add(
u128::from(ctx.accounts.msrm.pool_mint.supply)
.checked_mul(1_000_000)
.unwrap(),
)
.unwrap();
// Total reward split between both the SRM and MSRM stake pools.
let total_reward_amount = u128::from(ctx.accounts.stake.amount);
// Proportion of the reward going to the srm pool.
//
// total_reward_amount * (srm_pool_value / total_pool_value)
//
let srm_amount: u64 = u128::from(ctx.accounts.srm.pool_mint.supply)
.checked_mul(500)
.unwrap()
.checked_mul(total_reward_amount)
.unwrap()
.checked_div(total_pool_value)
.unwrap()
.try_into()
.map_err(|_| error!(ErrorCode::U128CannotConvert))?;
// Proportion of the reward going to the msrm pool.
//
// total_reward_amount * (msrm_pool_value / total_pool_value)
//
let msrm_amount = u128::from(ctx.accounts.msrm.pool_mint.supply)
.checked_mul(total_reward_amount)
.unwrap()
.checked_div(total_pool_value)
.unwrap()
.try_into()
.map_err(|_| error!(ErrorCode::U128CannotConvert))?;
// SRM drop.
{
// Drop locked reward.
let (_, nonce) = Pubkey::find_program_address(
&[
ctx.accounts.srm.registrar.to_account_info().key.as_ref(),
ctx.accounts.srm.vendor.to_account_info().key.as_ref(),
],
ctx.accounts.token_program.key,
);
registry::cpi::drop_reward(
ctx.accounts.into_srm_reward().with_signer(&[&seeds[..]]),
locked_kind.clone(),
srm_amount.try_into().unwrap(),
expiry_ts,
expiry_receiver,
nonce,
)?;
// Drop unlocked reward.
registry::cpi::drop_reward(
ctx.accounts.into_srm_reward().with_signer(&[&seeds[..]]),
RewardVendorKind::Unlocked,
srm_amount,
expiry_ts,
expiry_receiver,
nonce,
)?;
}
// MSRM drop.
{
// Drop locked reward.
let (_, nonce) = Pubkey::find_program_address(
&[
ctx.accounts.msrm.registrar.to_account_info().key.as_ref(),
ctx.accounts.msrm.vendor.to_account_info().key.as_ref(),
],
ctx.accounts.token_program.key,
);
registry::cpi::drop_reward(
ctx.accounts.into_msrm_reward().with_signer(&[&seeds[..]]),
locked_kind,
msrm_amount,
expiry_ts,
expiry_receiver,
nonce,
)?;
// Drop unlocked reward.
registry::cpi::drop_reward(
ctx.accounts.into_msrm_reward().with_signer(&[&seeds[..]]),
RewardVendorKind::Unlocked,
msrm_amount,
expiry_ts,
expiry_receiver,
nonce,
)?;
}
Ok(())
}
}
// Context accounts.
#[derive(Accounts)]
#[instruction(bumps: OfficerBumps)]
pub struct CreateOfficer<'info> {
#[account(
init,
seeds = [dex_program.key.as_ref()],
bump,
payer = authority,
space = Officer::LEN + 8
)]
officer: Box<Account<'info, Officer>>,
#[account(
init,
seeds = [b"token", officer.key().as_ref(), srm_mint.key().as_ref()],
bump,
payer = authority,
token::mint = srm_mint,
token::authority = officer
)]
srm_vault: Box<Account<'info, TokenAccount>>,
#[account(
init,
seeds = [b"token", officer.key().as_ref(), usdc_mint.key().as_ref()],
bump,
payer = authority,
token::mint = usdc_mint,
token::authority = officer
)]
usdc_vault: Box<Account<'info, TokenAccount>>,
#[account(
init,
seeds = [b"stake", officer.key().as_ref()],
bump,
payer = authority,
token::mint = srm_mint,
token::authority = officer
)]
stake: Box<Account<'info, TokenAccount>>,
#[account(
init,
seeds = [b"treasury", officer.key().as_ref()],
bump,
payer = authority,
token::mint = srm_mint,
token::authority = officer
)]
treasury: Box<Account<'info, TokenAccount>>,
#[account(mut)]
authority: Signer<'info>,
#[cfg_attr(
not(feature = "test"),
account(address = mint::SRM),
)]
srm_mint: Box<Account<'info, Mint>>,
#[cfg_attr(
not(feature = "test"),
account(address = mint::USDC),
)]
usdc_mint: Box<Account<'info, Mint>>,
dex_program: Program<'info, Dex>,
swap_program: Program<'info, Swap>,
system_program: Program<'info, System>,
token_program: Program<'info, Token>,
rent: Sysvar<'info, Rent>,
}
#[derive(Accounts)]
#[instruction(bump: u8)]
pub struct AuthorizeMarket<'info> {
#[account(has_one = authority)]
officer: Account<'info, Officer>,
authority: Signer<'info>,
#[account(
init,
payer = payer,
seeds = [b"market-auth", officer.key().as_ref(), market.key.as_ref()],
bump,
space = MarketAuth::LEN + 8
)]
market_auth: Account<'info, MarketAuth>,
#[account(mut)]
payer: Signer<'info>,
// Not read or written to so not validated.
market: UncheckedAccount<'info>,
system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct RevokeMarket<'info> {
#[account(has_one = authority)]
pub officer: Account<'info, Officer>,
pub authority: Signer<'info>,
#[account(mut, close = payer)]
pub auth: Account<'info, MarketAuth>,
pub payer: Signer<'info>,
}
#[derive(Accounts)]
#[instruction(bump: u8)]
pub struct CreateOfficerToken<'info> {
officer: Account<'info, Officer>,
#[account(
init,
seeds = [b"token", officer.key().as_ref(), mint.key().as_ref()],
bump,
token::mint = mint,
token::authority = officer,
payer = payer
)]
token: Account<'info, TokenAccount>,
mint: Account<'info, Mint>,
#[account(mut)]
payer: Signer<'info>,
system_program: Program<'info, System>,
token_program: Program<'info, Token>,
rent: Sysvar<'info, Rent>,
}
#[derive(Accounts)]
#[instruction(bump: u8)]
pub struct CreateOfficerOpenOrders<'info> {
officer: Account<'info, Officer>,
#[account(
init,
seeds = [b"open-orders", officer.key().as_ref(), market.key.as_ref()],
bump,
space = 12 + size_of::<OpenOrders>(),
payer = payer,
owner = dex::ID,
)]
open_orders: UncheckedAccount<'info>,
#[account(mut)]
payer: Signer<'info>,
dex_program: Program<'info, Dex>,
system_program: Program<'info, System>,
rent: Sysvar<'info, Rent>,
// Used for CPI. Not read or written so not validated.
market: UncheckedAccount<'info>,
}
#[derive(Accounts)]
pub struct SetDistribution<'info> {
#[account(has_one = authority)]
officer: Account<'info, Officer>,
authority: Signer<'info>,
}
#[derive(Accounts)]
pub struct SweepFees<'info> {
#[account(
seeds = [dex.dex_program.key.as_ref()],
bump = officer.bumps.bump,
)]
officer: Account<'info, Officer>,
#[account(
mut,
seeds = [b"token", officer.key().as_ref(), mint.key().as_ref()],
bump,
)]
sweep_vault: Account<'info, TokenAccount>,
mint: Account<'info, Mint>,
dex: DexAccounts<'info>,
}
// DexAccounts are safe because they are used for CPI only.
// They are not read or written and so are not validated.
#[derive(Accounts)]
pub struct DexAccounts<'info> {
#[account(mut)]
market: UncheckedAccount<'info>,
#[account(mut)]
pc_vault: UncheckedAccount<'info>,
sweep_authority: UncheckedAccount<'info>,
vault_signer: UncheckedAccount<'info>,
dex_program: Program<'info, Dex>,
token_program: Program<'info, Token>,
}
#[derive(Accounts)]
pub struct SwapToUsdc<'info> {
#[account(
seeds = [dex_program.key.as_ref()],
bump = officer.bumps.bump,
)]
officer: Box<Account<'info, Officer>>,
market: DexMarketAccounts<'info>,
#[account(
seeds = [b"market-auth", officer.key().as_ref(), market.market.key.as_ref()],
bump = market_auth.bump,
)]
market_auth: Account<'info, MarketAuth>,
#[account(
mut,
constraint = &officer.treasury != &from_vault.key(),
constraint = &officer.stake != &from_vault.key(),
)]
from_vault: Box<Account<'info, TokenAccount>>,
#[account(
mut,
seeds = [b"token", officer.key().as_ref(), usdc_mint.key().as_ref()],
bump,
)]
usdc_vault: Box<Account<'info, TokenAccount>>,
#[cfg_attr(not(feature = "test"), account(address = mint::USDC))]
usdc_mint: Box<Account<'info, Mint>>,
swap_program: Program<'info, Swap>,
dex_program: Program<'info, Dex>,
token_program: Program<'info, Token>,
#[account(address = tx_instructions::ID)]
instructions: UncheckedAccount<'info>,
rent: Sysvar<'info, Rent>,
}
#[derive(Accounts)]
#[instruction(bump: u8)]
pub struct SwapToSrm<'info> {
#[account(
seeds = [dex_program.key.as_ref()],
bump = officer.bumps.bump,
)]
officer: Box<Account<'info, Officer>>,
market: DexMarketAccounts<'info>,
#[account(
seeds = [b"market-auth", officer.key().as_ref(), market.market.key.as_ref()],
bump = market_auth.bump,
)]
market_auth: Account<'info, MarketAuth>,
#[account(
mut,
seeds = [b"token", officer.key().as_ref(), usdc_mint.key().as_ref()],
bump,
)]
usdc_vault: Box<Account<'info, TokenAccount>>,
#[account(
mut,
seeds = [b"token", officer.key().as_ref(), srm_mint.key().as_ref()],
bump,
)]
srm_vault: Box<Account<'info, TokenAccount>>,
#[cfg_attr(not(feature = "test"), account(address = mint::SRM))]
srm_mint: Box<Account<'info, Mint>>,
#[cfg_attr(not(feature = "test"), account(address = mint::USDC))]
usdc_mint: Box<Account<'info, Mint>>,
swap_program: Program<'info, Swap>,
dex_program: Program<'info, Dex>,
token_program: Program<'info, Token>,
#[account(address = tx_instructions::ID)]
instructions: UncheckedAccount<'info>,
rent: Sysvar<'info, Rent>,
}
// Dex accounts are used for CPI only.
// They are not read or written and so are not validated.
#[derive(Accounts)]
pub struct DexMarketAccounts<'info> {
#[account(mut)]
market: UncheckedAccount<'info>,
#[account(mut)]
open_orders: UncheckedAccount<'info>,
#[account(mut)]
request_queue: UncheckedAccount<'info>,
#[account(mut)]
event_queue: UncheckedAccount<'info>,
#[account(mut)]
bids: UncheckedAccount<'info>,
#[account(mut)]
asks: UncheckedAccount<'info>,
// The `spl_token::Account` that funds will be taken from, i.e., transferred
// from the user into the market's vault.
//
// For bids, this is the base currency. For asks, the quote.
#[account(mut)]
order_payer_token_account: UncheckedAccount<'info>,
// Also known as the "base" currency. For a given A/B market,
// this is the vault for the A mint.
#[account(mut)]
coin_vault: UncheckedAccount<'info>,
// Also known as the "quote" currency. For a given A/B market,
// this is the vault for the B mint.
#[account(mut)]
pc_vault: UncheckedAccount<'info>,
// PDA owner of the DEX's token accounts for base + quote currencies.
vault_signer: UncheckedAccount<'info>,
}
#[derive(Accounts)]
pub struct Distribute<'info> {
#[account(
has_one = srm_vault,
has_one = treasury,
has_one = stake,
)]
officer: Box<Account<'info, Officer>>,
#[account(mut)]
treasury: Box<Account<'info, TokenAccount>>,
#[account(mut)]
stake: Account<'info, TokenAccount>,
#[account(mut)]
srm_vault: Account<'info, TokenAccount>,
#[account(mut)]
srm_mint: Account<'info, Mint>,
token_program: Program<'info, Token>,
dex_program: Program<'info, Dex>,
}
#[derive(Accounts)]
pub struct DropStakeReward<'info> {
#[account(
has_one = stake,
constraint = srm.registrar.key == &officer.registrar,
constraint = msrm.registrar.key == &officer.msrm_registrar,
)]
officer: Box<Account<'info, Officer>>,
#[account(
seeds = [b"stake", officer.key().as_ref()],
bump = officer.bumps.stake,
)]
stake: Box<Account<'info, TokenAccount>>,
#[cfg_attr(
not(feature = "test"),
account(address = mint::SRM),
)]
mint: UncheckedAccount<'info>,
srm: DropStakeRewardPool<'info>,
msrm: DropStakeRewardPool<'info>,
msrm_registrar: Box<Account<'info, Registrar>>,
token_program: Program<'info, Token>,
registry_program: Program<'info, Registry>,
lockup_program: Program<'info, Lockup>,
dex_program: Program<'info, Dex>,
clock: Sysvar<'info, Clock>,
rent: Sysvar<'info, Rent>,
}
// Don't bother doing validation on the individual accounts. Allow the stake
// program to handle it.
#[derive(Accounts)]
pub struct DropStakeRewardPool<'info> {
registrar: UncheckedAccount<'info>,
reward_event_q: UncheckedAccount<'info>,
pool_mint: Account<'info, Mint>,
vendor: UncheckedAccount<'info>,
vendor_vault: UncheckedAccount<'info>,
}
// Accounts.
/// Officer represents a deployed instance of the CFO mechanism. It is tied
/// to a single deployment of the dex program.
///
/// PDA - [dex_program_id].
#[account]
pub struct Officer {
// Priviledged account.
pub authority: Pubkey, // 32
// Vault holding the officer's SRM tokens prior to distribution.
pub srm_vault: Pubkey, // 32
// Escrow SRM vault holding tokens which are dropped onto stakers.
pub stake: Pubkey, // 32
// SRM token account to send treasury earned tokens to.
pub treasury: Pubkey, // 32
// Defines the fee distribution, i.e., what percent each fee category gets.
pub distribution: Distribution, // Distribution::LEN
// Swap frontend for the dex.
pub swap_program: Pubkey, // 32
// Dex program the officer is associated with.
pub dex_program: Pubkey, // 32
// SRM stake pool address
pub registrar: Pubkey, // 32
// MSRM stake pool address.
pub msrm_registrar: Pubkey, // 32
// Bump seeds for pdas.
pub bumps: OfficerBumps, // OfficerBumps::LEN
}
impl Officer {
pub const LEN: usize = 8 * 32 + Distribution::LEN + OfficerBumps::LEN;
}
/// MarketAuth represents an authorization token created by the Officer
/// authority. This is used as an authorization token which allows one to
/// permissionlessly invoke the swap instructions on the market. Without this
/// one would be able to create their own market with prices unfavorable
/// to the smart contract (and subsequently swap on it).
///
/// Because a PDA is used here, the account existing (without a tombstone) is
/// proof of the validity of a given market, which means that anyone can use
/// the vault here to swap.
///
/// PDA - [b"market-auth", officer, market_address]
#[account]
pub struct MarketAuth {
// Bump seed for this account's PDA.
pub bump: u8, // 1
}
impl MarketAuth {
pub const LEN: usize = 1;
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]
pub struct OfficerBumps {
pub bump: u8, // 1
pub srm: u8, // 1
pub usdc: u8, // 1
pub stake: u8, // 1
pub treasury: u8, // 1
}
impl OfficerBumps {
pub const LEN: usize = 5;
}
#[derive(AnchorSerialize, AnchorDeserialize, Default, Clone)]
pub struct Distribution {
burn: u8, // 1
stake: u8, // 1
treasury: u8, // 1
}
impl Distribution {
pub const LEN: usize = 3;
}
// CpiContext transformations.
impl<'info> From<&CreateOfficerOpenOrders<'info>>
for CpiContext<'_, '_, '_, 'info, dex::InitOpenOrders<'info>>
{
fn from(accs: &CreateOfficerOpenOrders<'info>) -> Self {
let program = accs.dex_program.to_account_info();
let accounts = dex::InitOpenOrders {
open_orders: accs.open_orders.to_account_info(),
authority: accs.officer.to_account_info(),
market: accs.market.to_account_info(),
rent: accs.rent.to_account_info(),
};
CpiContext::new(program, accounts)
}
}
impl<'info> From<&SweepFees<'info>> for CpiContext<'_, '_, '_, 'info, dex::SweepFees<'info>> {
fn from(sweep: &SweepFees<'info>) -> Self {
let program = sweep.dex.dex_program.to_account_info();
let accounts = dex::SweepFees {
market: sweep.dex.market.to_account_info(),
pc_vault: sweep.dex.pc_vault.to_account_info(),
sweep_authority: sweep.dex.sweep_authority.to_account_info(),
sweep_receiver: sweep.sweep_vault.to_account_info(),
vault_signer: sweep.dex.vault_signer.to_account_info(),
token_program: sweep.dex.token_program.to_account_info(),
};
CpiContext::new(program.to_account_info(), accounts)
}
}
impl<'info> From<&SwapToSrm<'info>>
for CpiContext<'_, '_, '_, 'info, swap::cpi::accounts::Swap<'info>>
{
fn from(accs: &SwapToSrm<'info>) -> Self {
let program = accs.swap_program.to_account_info();
let accounts = swap::cpi::accounts::Swap {
market: swap::cpi::accounts::MarketAccounts {
market: accs.market.market.to_account_info(),
open_orders: accs.market.open_orders.to_account_info(),
request_queue: accs.market.request_queue.to_account_info(),
event_queue: accs.market.event_queue.to_account_info(),
bids: accs.market.bids.to_account_info(),
asks: accs.market.asks.to_account_info(),
order_payer_token_account: accs.market.order_payer_token_account.to_account_info(),
coin_vault: accs.market.coin_vault.to_account_info(),
pc_vault: accs.market.pc_vault.to_account_info(),
vault_signer: accs.market.vault_signer.to_account_info(),
coin_wallet: accs.srm_vault.to_account_info(),
},
authority: accs.officer.to_account_info(),
pc_wallet: accs.usdc_vault.to_account_info(),
dex_program: accs.dex_program.to_account_info(),
token_program: accs.token_program.to_account_info(),
rent: accs.rent.to_account_info(),
};
CpiContext::new(program.to_account_info(), accounts)
}
}
impl<'info> From<&SwapToUsdc<'info>>
for CpiContext<'_, '_, '_, 'info, swap::cpi::accounts::Swap<'info>>
{
fn from(accs: &SwapToUsdc<'info>) -> Self {
let program = accs.swap_program.to_account_info();
let accounts = swap::cpi::accounts::Swap {
market: swap::cpi::accounts::MarketAccounts {
market: accs.market.market.to_account_info(),
open_orders: accs.market.open_orders.to_account_info(),
request_queue: accs.market.request_queue.to_account_info(),
event_queue: accs.market.event_queue.to_account_info(),
bids: accs.market.bids.to_account_info(),
asks: accs.market.asks.to_account_info(),
order_payer_token_account: accs.market.order_payer_token_account.to_account_info(),
coin_vault: accs.market.coin_vault.to_account_info(),
pc_vault: accs.market.pc_vault.to_account_info(),
vault_signer: accs.market.vault_signer.to_account_info(),
coin_wallet: accs.from_vault.to_account_info(),
},
authority: accs.officer.to_account_info(),
pc_wallet: accs.usdc_vault.to_account_info(),
dex_program: accs.dex_program.to_account_info(),
token_program: accs.token_program.to_account_info(),
rent: accs.rent.to_account_info(),
};
CpiContext::new(program.to_account_info(), accounts)
}
}
impl<'info> Distribute<'info> {
fn into_burn(&self) -> CpiContext<'_, '_, '_, 'info, token::Burn<'info>> {
let program = self.token_program.to_account_info();
let accounts = token::Burn {
mint: self.srm_mint.to_account_info(),
from: self.srm_vault.to_account_info(),
authority: self.officer.to_account_info(),
};
CpiContext::new(program, accounts)
}
fn into_stake_transfer(&self) -> CpiContext<'_, '_, '_, 'info, token::Transfer<'info>> {
let program = self.token_program.to_account_info();
let accounts = token::Transfer {
from: self.srm_vault.to_account_info(),
to: self.stake.to_account_info(),
authority: self.officer.to_account_info(),
};
CpiContext::new(program, accounts)
}
fn into_treasury_transfer(&self) -> CpiContext<'_, '_, '_, 'info, token::Transfer<'info>> {
let program = self.token_program.to_account_info();
let accounts = token::Transfer {
from: self.srm_vault.to_account_info(),
to: self.treasury.to_account_info(),
authority: self.officer.to_account_info(),
};
CpiContext::new(program, accounts)
}
}
impl<'info> DropStakeReward<'info> {
fn into_srm_reward(
&self,
) -> CpiContext<'_, '_, '_, 'info, registry::cpi::accounts::DropReward<'info>> {
let program = self.registry_program.clone();
let accounts = registry::cpi::accounts::DropReward {
registrar: self.srm.registrar.to_account_info(),
reward_event_q: self.srm.reward_event_q.to_account_info(),
pool_mint: self.srm.pool_mint.to_account_info(),
vendor: self.srm.vendor.to_account_info(),
vendor_vault: self.srm.vendor_vault.to_account_info(),
depositor: self.stake.to_account_info(),
depositor_authority: self.officer.to_account_info(),
token_program: self.token_program.to_account_info(),
clock: self.clock.to_account_info(),
rent: self.rent.to_account_info(),
};
CpiContext::new(program.to_account_info(), accounts)
}
fn into_msrm_reward(
&self,
) -> CpiContext<'_, '_, '_, 'info, registry::cpi::accounts::DropReward<'info>> {
let program = self.registry_program.clone();
let accounts = registry::cpi::accounts::DropReward {
registrar: self.msrm.registrar.to_account_info(),
reward_event_q: self.msrm.reward_event_q.to_account_info(),
pool_mint: self.msrm.pool_mint.to_account_info(),
vendor: self.msrm.vendor.to_account_info(),
vendor_vault: self.msrm.vendor_vault.to_account_info(),
depositor: self.stake.to_account_info(),
depositor_authority: self.officer.to_account_info(),
token_program: self.token_program.to_account_info(),
clock: self.clock.to_account_info(),
rent: self.rent.to_account_info(),
};
CpiContext::new(program.to_account_info(), accounts)
}
}
// Events.
#[event]
pub struct DistributionDidChange {
distribution: Distribution,
}
#[event]
pub struct OfficerDidCreate {
pubkey: Pubkey,
}
// Error.
#[error_code]
pub enum ErrorCode {
#[msg("Distribution does not add to 100")]
InvalidDistribution,
#[msg("u128 cannot be converted into u64")]
U128CannotConvert,
#[msg("Only one instruction is allowed for this transaction")]
TooManyInstructions,
#[msg("Not enough SRM has been accumulated to distribute")]
InsufficientDistributionAmount,
#[msg("Must drop more SRM onto the stake pool")]
InsufficientStakeReward,
}
// Access control.
fn is_distribution_valid(d: &Distribution) -> Result<()> {
if d.burn + d.stake + d.treasury != 100 {
return err!(ErrorCode::InvalidDistribution);
}
Ok(())
}
fn is_distribution_ready(accounts: &Distribute) -> Result<()> {
if accounts.srm_vault.amount < 1_000_000 {
return err!(ErrorCode::InsufficientDistributionAmount);
}
Ok(())
}
// `ixs` must be the Instructions sysvar.
fn is_not_trading(ixs: &UncheckedAccount) -> Result<()> {
let data = ixs.try_borrow_data()?;
match tx_instructions::load_instruction_at(1, &data) {
Ok(_) => err!(ErrorCode::TooManyInstructions),
Err(_) => Ok(()),
}
}
fn is_stake_reward_ready(accounts: &DropStakeReward) -> Result<()> {
// Min drop is 15,0000 SRM.
let min_reward: u64 = 15_000_000_000;
if accounts.stake.amount < min_reward {
return err!(ErrorCode::InsufficientStakeReward);
}
Ok(())
}
// Redefintions.
//
// The following types are redefined so that they can be parsed into the IDL,
// since Anchor doesn't yet support idl parsing across multiple crates.
#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct ExchangeRate {
rate: u64,
from_decimals: u8,
quote_decimals: u8,
strict: bool,
}
impl From<ExchangeRate> for swap::ExchangeRate {
fn from(e: ExchangeRate) -> Self {
let ExchangeRate {
rate,
from_decimals,
quote_decimals,
strict,
} = e;
Self {
rate,
from_decimals,
quote_decimals,
strict,
}
}
}

View File

@ -1,19 +0,0 @@
cleanup() {
pkill -P $$ || true
wait || true
}
trap_add() {
trap_add_cmd=$1; shift || fatal "${FUNCNAME} usage error"
for trap_add_name in "$@"; do
trap -- "$(
extract_trap_cmd() { printf '%s\n' "${3:-}"; }
eval "extract_trap_cmd $(trap -p "${trap_add_name}")"
printf '%s\n' "${trap_add_cmd}"
)" "${trap_add_name}" \
|| fatal "unable to add to trap ${trap_add_name}"
done
}
declare -f -t trap_add
trap_add 'cleanup' EXIT

View File

@ -1,34 +0,0 @@
#!/usr/bin/env node
const process = require("process");
const fs = require("fs");
const anchor = require("@project-serum/anchor");
const { Market, OpenOrders } = require("@project-serum/serum");
const Account = anchor.web3.Account;
const Program = anchor.Program;
const provider = anchor.AnchorProvider.local();
const secret = JSON.parse(fs.readFileSync("./scripts/market-maker.json"));
const MARKET_MAKER = new Account(secret);
const PublicKey = anchor.web3.PublicKey;
const DEX_PID = new PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin");
async function main() {
const market = new PublicKey(process.argv[2]);
while (true) {
let marketClient = await Market.load(
provider.connection,
market,
{ commitment: "processed" },
DEX_PID
);
console.log("Fees: ", marketClient._decoded.quoteFeesAccrued.toString());
await sleep(3000);
}
}
main();
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

View File

@ -1,23 +0,0 @@
#!/usr/bin/env node
// Script to list a market, logging the address to stdout.
const utils = require("../tests/utils");
const fs = require("fs");
const anchor = require("@project-serum/anchor");
const provider = anchor.AnchorProvider.local();
// hack so we don't have to update serum-common library
// to the new AnchorProvider class and Provider interface
provider.send = provider.sendAndConfirm;
async function main() {
ORDERBOOK_ENV = await utils.initMarket({
provider,
});
const out = {
market: ORDERBOOK_ENV.marketA._decoded.ownAddress.toString(),
};
console.log(JSON.stringify(out));
}
main();

View File

@ -1,58 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
source scripts/common.sh
DEX_PID="9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"
PAYER_FILEPATH="$HOME/.config/solana/id.json"
CRANK="/home/armaniferrante/Documents/code/src/github.com/project-serum/serum-dex/target/debug/crank"
VALIDATOR_OUT="./validator-stdout.txt"
CRANK_LOGS="crank-logs.txt"
CRANK_STDOUT="crank-stdout.txt"
TRADE_BOT_STDOUT="trade-bot-stdout.txt"
FEES_STDOUT="fees.txt"
main () {
echo "Cleaning old output files..."
rm -rf test-ledger
rm -f $TRADE_BOT_STDOUT
rm -f $FEES_STDOUT
rm -f $VALIDATOR_OUT
rm -f $CRANK_LOGS && touch $CRANK_LOGS
echo "Starting local network..."
solana-test-validator \
--bpf-program 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin ./deps/serum-dex/dex/target/deploy/serum_dex.so \
--bpf-program 22Y43yTVxuUkoRKdm9thyRhQ3SdgQS7c7kB6UNCiaczD ./deps/swap/target/deploy/swap.so \
--bpf-program GrAkKfEpTKQuVHG2Y97Y2FF4i7y7Q5AHLK94JBy7Y5yv ./deps/stake/target/deploy/registry.so \
--bpf-program 6ebQNeTPZ1j7k3TtkCCtEPRvG7GQsucQrZ7sSEDQi9Ks ./deps/stake/target/deploy/lockup.so \
--bpf-program 5CHQcwNhkFiFXXM8HakHi8cB7AKP3M3GPdEBDeRJBWQq ./target/deploy/cfo.so > $VALIDATOR_OUT &
sleep 2
echo "Listing market..."
market=$(./scripts/list-market.js | jq -r .market)
sleep 2
echo "Market listed $market"
echo "Running crank..."
$CRANK localnet consume-events \
-c $market \
-d $DEX_PID -e 5 \
--log-directory $CRANK_LOGS \
--market $market \
--num-workers 1 \
--payer $PAYER_FILEPATH \
--pc-wallet $market > $CRANK_STDOUT &
echo "Running trade bot..."
./scripts/trade-bot.js $market > $TRADE_BOT_STDOUT &
echo "Running fees listener..."
./scripts/fees.js $market > $FEES_STDOUT &
echo "Localnet running..."
echo "Ctl-c to exit."
wait
}
main

View File

@ -1 +0,0 @@
[13,174,53,150,78,228,12,98,170,254,212,211,125,193,2,241,97,137,49,209,189,199,27,215,220,65,57,203,215,93,105,203,217,32,5,194,157,118,162,47,102,126,235,65,99,80,56,231,217,114,25,225,239,140,169,92,150,146,211,218,183,139,9,104]

View File

@ -1,16 +0,0 @@
#!/usr/bin/env node
// Script to infinitely post orders that are immediately filled.
const process = require("process");
const anchor = require("@project-serum/anchor");
const PublicKey = anchor.web3.PublicKey;
const { runTradeBot } = require("../tests/utils");
async function main() {
const market = new PublicKey(process.argv[2]);
const provider = anchor.AnchorProvider.local();
runTradeBot(market, provider);
}
main();

View File

@ -1,515 +0,0 @@
const { assert } = require("chai");
const { Token } = require("@solana/spl-token");
const anchor = require("@project-serum/anchor");
const serumCmn = require("@project-serum/common");
const { Market } = require("@project-serum/serum");
const utf8 = anchor.utils.bytes.utf8;
const { PublicKey, SystemProgram, Keypair, SYSVAR_RENT_PUBKEY } = anchor.web3;
const utils = require("./utils");
const { setupStakePool } = require("./utils/stake");
const DEX_PID = new PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin");
const SWAP_PID = new PublicKey("22Y43yTVxuUkoRKdm9thyRhQ3SdgQS7c7kB6UNCiaczD");
const TOKEN_PID = new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
const REGISTRY_PID = new PublicKey(
"GrAkKfEpTKQuVHG2Y97Y2FF4i7y7Q5AHLK94JBy7Y5yv"
);
const LOCKUP_PID = new PublicKey(
"6ebQNeTPZ1j7k3TtkCCtEPRvG7GQsucQrZ7sSEDQi9Ks"
);
const SYSVAR_INSTRUCTIONS_PUBKEY = new PublicKey(
"Sysvar1nstructions1111111111111111111111111"
);
const FEES = "6160355581";
describe("cfo", () => {
const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
const program = anchor.workspace.Cfo;
// hack so we don't have to update serum-common library
// to the new AnchorProvider class and Provider interface
program.provider.send = provider.sendAndConfirm;
const sweepAuthority = program.provider.wallet.publicKey;
let officer, srmVault, usdcVault, bVault, stake, treasury;
let officerBump, srmBump, usdcBump, bBump, stakeBump, treasuryBump;
let openOrders, openOrdersBump;
let openOrdersB, openOrdersBumpB;
let USDC_TOKEN_CLIENT, A_TOKEN_CLIENT, B_TOKEN_CLIENT;
let officerAccount;
let marketAClient, marketBClient;
let marketAuth, marketAuthBump;
let marketAuthB, marketAuthBumpB;
let distribution;
// Accounts used to setup the orderbook.
let ORDERBOOK_ENV,
// Accounts used for A -> USDC swap transactions.
SWAP_A_USDC_ACCOUNTS,
// Accounts used for USDC -> A swap transactions.
SWAP_USDC_A_ACCOUNTS,
// Serum DEX vault PDA for market A/USDC.
marketAVaultSigner,
// Serum DEX vault PDA for market B/USDC.
marketBVaultSigner;
let registrar, msrmRegistrar;
it("BOILERPLATE: Sets up a market with funded fees", async () => {
ORDERBOOK_ENV = await utils.initMarket({
provider: program.provider,
});
console.log("Token A: ", ORDERBOOK_ENV.marketA.baseMintAddress.toString());
console.log(
"Token USDC: ",
ORDERBOOK_ENV.marketA.quoteMintAddress.toString()
);
USDC_TOKEN_CLIENT = new Token(
program.provider.connection,
ORDERBOOK_ENV.usdc,
TOKEN_PID,
program.provider.wallet.payer
);
SRM_TOKEN_CLIENT = new Token(
program.provider.connection,
ORDERBOOK_ENV.mintA,
TOKEN_PID,
program.provider.wallet.payer
);
B_TOKEN_CLIENT = new Token(
program.provider.connection,
ORDERBOOK_ENV.mintB,
TOKEN_PID,
program.provider.wallet.payer
);
await USDC_TOKEN_CLIENT.transfer(
ORDERBOOK_ENV.godUsdc,
ORDERBOOK_ENV.marketA._decoded.quoteVault,
program.provider.wallet.payer,
[],
10000000000000
);
const tokenAccount = await USDC_TOKEN_CLIENT.getAccountInfo(
ORDERBOOK_ENV.marketA._decoded.quoteVault
);
assert.strictEqual(tokenAccount.amount.toString(), "10000902263700");
});
it("BOILERPLATE: Executes trades to generate fees", async () => {
await utils.runTradeBot(
ORDERBOOK_ENV.marketA._decoded.ownAddress,
program.provider,
1
);
marketAClient = await Market.load(
program.provider.connection,
ORDERBOOK_ENV.marketA.address,
{ commitment: "processed" },
DEX_PID
);
marketBClient = await Market.load(
program.provider.connection,
ORDERBOOK_ENV.marketB.address,
{ commitment: "processed" },
DEX_PID
);
assert.strictEqual(
marketAClient._decoded.quoteFeesAccrued.toString(),
FEES
);
});
it("BOILERPLATE: Sets up the staking pools", async () => {
await setupStakePool(ORDERBOOK_ENV.mintA, ORDERBOOK_ENV.godA);
registrar = ORDERBOOK_ENV.usdc;
msrmRegistrar = registrar;
});
it("BOILERPLATE: Finds PDA addresses", async () => {
const [_officer, _officerBump] = await PublicKey.findProgramAddress(
[DEX_PID.toBuffer()],
program.programId
);
const [_openOrders, _openOrdersBump] = await PublicKey.findProgramAddress(
[
utf8.encode("open-orders"),
_officer.toBuffer(),
ORDERBOOK_ENV.marketA.address.toBuffer(),
],
program.programId
);
const [_openOrdersB, _openOrdersBumpB] = await PublicKey.findProgramAddress(
[
utf8.encode("open-orders"),
_officer.toBuffer(),
ORDERBOOK_ENV.marketB.address.toBuffer(),
],
program.programId
);
const [_srmVault, _srmBump] = await PublicKey.findProgramAddress(
[
utf8.encode("token"),
_officer.toBuffer(),
ORDERBOOK_ENV.mintA.toBuffer(),
],
program.programId
);
const [_bVault, _bBump] = await PublicKey.findProgramAddress(
[
utf8.encode("token"),
_officer.toBuffer(),
ORDERBOOK_ENV.mintB.toBuffer(),
],
program.programId
);
const [_usdcVault, _usdcBump] = await PublicKey.findProgramAddress(
[
utf8.encode("token"),
_officer.toBuffer(),
ORDERBOOK_ENV.usdc.toBuffer(),
],
program.programId
);
const [_stake, _stakeBump] = await PublicKey.findProgramAddress(
[utf8.encode("stake"), _officer.toBuffer()],
program.programId
);
const [_treasury, _treasuryBump] = await PublicKey.findProgramAddress(
[utf8.encode("treasury"), _officer.toBuffer()],
program.programId
);
const [_marketAuth, _marketAuthBump] = await PublicKey.findProgramAddress(
[
utf8.encode("market-auth"),
_officer.toBuffer(),
ORDERBOOK_ENV.marketA.address.toBuffer(),
],
program.programId
);
const [_marketAuthB, _marketAuthBumpB] = await PublicKey.findProgramAddress(
[
utf8.encode("market-auth"),
_officer.toBuffer(),
ORDERBOOK_ENV.marketB.address.toBuffer(),
],
program.programId
);
officer = _officer;
officerBump = _officerBump;
openOrders = _openOrders;
openOrdersBump = _openOrdersBump;
openOrdersB = _openOrdersB;
openOrdersBumpB = _openOrdersBumpB;
srmVault = _srmVault;
srmBump = _srmBump;
usdcVault = _usdcVault;
usdcBump = _usdcBump;
bVault = _bVault;
bBump = _bBump;
stake = _stake;
stakeBump = _stakeBump;
treasury = _treasury;
treasuryBump = _treasuryBump;
marketAuth = _marketAuth;
marketAuthBump = _marketAuthBump;
marketAuthB = _marketAuthB;
marketAuthBumpB = _marketAuthBumpB;
});
it("Creates a CFO!", async () => {
distribution = {
burn: 80,
stake: 20,
treasury: 0,
};
const bumps = {
bump: officerBump,
srm: srmBump,
usdc: usdcBump,
stake: stakeBump,
treasury: treasuryBump,
};
await program.methods
.createOfficer(bumps, distribution, registrar, msrmRegistrar)
.accounts({
officer,
srmVault,
usdcVault,
stake,
treasury,
srmMint: ORDERBOOK_ENV.mintA,
usdcMint: ORDERBOOK_ENV.usdc,
authority: program.provider.wallet.publicKey,
dexProgram: DEX_PID,
swapProgram: SWAP_PID,
tokenProgram: TOKEN_PID,
systemProgram: SystemProgram.programId,
rent: SYSVAR_RENT_PUBKEY,
})
.rpc();
officerAccount = await program.account.officer.fetch(officer);
assert.isTrue(
officerAccount.authority.equals(program.provider.wallet.publicKey)
);
assert.strictEqual(
JSON.stringify(officerAccount.distribution),
JSON.stringify(distribution)
);
});
it("Creates a token account for the officer associated with the market", async () => {
await program.methods
.createOfficerToken(bBump)
.accounts({
officer,
token: bVault,
mint: ORDERBOOK_ENV.mintB,
payer: program.provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
tokenProgram: TOKEN_PID,
rent: SYSVAR_RENT_PUBKEY,
})
.rpc();
const tokenAccount = await B_TOKEN_CLIENT.getAccountInfo(bVault);
assert.strictEqual(tokenAccount.state, 1);
assert.isTrue(tokenAccount.isInitialized);
});
it("Creates an open orders account for the officer", async () => {
await program.methods
.createOfficerOpenOrders(openOrdersBump)
.accounts({
officer,
openOrders,
payer: program.provider.wallet.publicKey,
dexProgram: DEX_PID,
systemProgram: SystemProgram.programId,
rent: SYSVAR_RENT_PUBKEY,
market: ORDERBOOK_ENV.marketA.address,
})
.rpc();
await program.rpc.createOfficerOpenOrders(openOrdersBumpB, {
accounts: {
officer,
openOrders: openOrdersB,
payer: program.provider.wallet.publicKey,
dexProgram: DEX_PID,
systemProgram: SystemProgram.programId,
rent: SYSVAR_RENT_PUBKEY,
market: ORDERBOOK_ENV.marketB.address,
},
});
});
it("Sweeps fees", async () => {
const [sweepVault, bump] = await PublicKey.findProgramAddress(
[utf8.encode("token"), officer.toBuffer(), ORDERBOOK_ENV.usdc.toBuffer()],
program.programId
);
const beforeTokenAccount = await serumCmn.getTokenAccount(
program.provider,
sweepVault
);
await program.methods
.sweepFees()
.accounts({
officer,
sweepVault,
mint: ORDERBOOK_ENV.usdc,
dex: {
market: ORDERBOOK_ENV.marketA._decoded.ownAddress,
pcVault: ORDERBOOK_ENV.marketA._decoded.quoteVault,
sweepAuthority,
vaultSigner: ORDERBOOK_ENV.marketAVaultSigner,
dexProgram: DEX_PID,
tokenProgram: TOKEN_PID,
},
})
.rpc();
const afterTokenAccount = await serumCmn.getTokenAccount(
program.provider,
sweepVault
);
assert.strictEqual(
afterTokenAccount.amount.sub(beforeTokenAccount.amount).toString(),
FEES
);
});
it("Creates a market auth token", async () => {
await program.methods
.authorizeMarket(marketAuthBump)
.accounts({
officer,
authority: program.provider.wallet.publicKey,
marketAuth,
payer: program.provider.wallet.publicKey,
market: ORDERBOOK_ENV.marketA.address,
systemProgram: SystemProgram.programId,
})
.rpc();
await program.methods
.authorizeMarket(marketAuthBumpB)
.accounts({
officer,
authority: program.provider.wallet.publicKey,
marketAuth: marketAuthB,
payer: program.provider.wallet.publicKey,
market: ORDERBOOK_ENV.marketB.address,
systemProgram: SystemProgram.programId,
})
.rpc();
});
it("Transfers into the mintB vault", async () => {
await B_TOKEN_CLIENT.transfer(
ORDERBOOK_ENV.godB,
bVault,
program.provider.wallet.payer,
[],
616035558100
);
});
it("Swaps from B token to USDC", async () => {
const bVaultBefore = await B_TOKEN_CLIENT.getAccountInfo(bVault);
const usdcVaultBefore = await USDC_TOKEN_CLIENT.getAccountInfo(usdcVault);
const minExchangeRate = {
rate: new anchor.BN(0),
fromDecimals: 6,
quoteDecimals: 6,
strict: false,
};
await program.methods
.swapToUsdc(minExchangeRate)
.accounts({
officer,
market: {
market: marketBClient.address,
openOrders: openOrdersB,
requestQueue: marketBClient.decoded.requestQueue,
eventQueue: marketBClient.decoded.eventQueue,
bids: marketBClient.decoded.bids,
asks: marketBClient.decoded.asks,
orderPayerTokenAccount: bVault,
coinVault: marketBClient.decoded.baseVault,
pcVault: marketBClient.decoded.quoteVault,
vaultSigner: ORDERBOOK_ENV.marketBVaultSigner,
},
marketAuth: marketAuthB,
usdcVault,
fromVault: bVault,
usdcMint: ORDERBOOK_ENV.usdc,
swapProgram: SWAP_PID,
dexProgram: DEX_PID,
tokenProgram: TOKEN_PID,
instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
rent: SYSVAR_RENT_PUBKEY,
})
.rpc();
const bVaultAfter = await B_TOKEN_CLIENT.getAccountInfo(bVault);
const usdcVaultAfter = await USDC_TOKEN_CLIENT.getAccountInfo(usdcVault);
assert.strictEqual(bVaultBefore.amount.toNumber(), 616035558100);
assert.strictEqual(usdcVaultBefore.amount.toNumber(), 6160355581);
assert.strictEqual(bVaultAfter.amount.toNumber(), 615884458100);
assert.strictEqual(usdcVaultAfter.amount.toNumber(), 7060634298);
});
it("Swaps to SRM", async () => {
const srmVaultBefore = await SRM_TOKEN_CLIENT.getAccountInfo(srmVault);
const usdcVaultBefore = await USDC_TOKEN_CLIENT.getAccountInfo(usdcVault);
const minExchangeRate = {
rate: new anchor.BN(0),
fromDecimals: 6,
quoteDecimals: 6,
strict: false,
};
await program.methods
.swapToSrm(minExchangeRate)
.accounts({
officer,
market: {
market: marketAClient.address,
openOrders,
requestQueue: marketAClient.decoded.requestQueue,
eventQueue: marketAClient.decoded.eventQueue,
bids: marketAClient.decoded.bids,
asks: marketAClient.decoded.asks,
orderPayerTokenAccount: usdcVault,
coinVault: marketAClient.decoded.baseVault,
pcVault: marketAClient.decoded.quoteVault,
vaultSigner: ORDERBOOK_ENV.marketAVaultSigner,
},
marketAuth,
usdcVault,
srmVault,
usdcMint: ORDERBOOK_ENV.usdc,
srmMint: ORDERBOOK_ENV.mintA,
swapProgram: SWAP_PID,
dexProgram: DEX_PID,
tokenProgram: TOKEN_PID,
instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
rent: SYSVAR_RENT_PUBKEY,
})
.rpc();
const srmVaultAfter = await SRM_TOKEN_CLIENT.getAccountInfo(srmVault);
const usdcVaultAfter = await USDC_TOKEN_CLIENT.getAccountInfo(usdcVault);
assert.strictEqual(srmVaultBefore.amount.toNumber(), 0);
assert.strictEqual(srmVaultAfter.amount.toNumber(), 1152000000);
assert.strictEqual(usdcVaultBefore.amount.toNumber(), 7060634298);
assert.strictEqual(usdcVaultAfter.amount.toNumber(), 530863);
});
it("Distributes the tokens to categories", async () => {
const srmVaultBefore = await SRM_TOKEN_CLIENT.getAccountInfo(srmVault);
const treasuryBefore = await SRM_TOKEN_CLIENT.getAccountInfo(treasury);
const stakeBefore = await SRM_TOKEN_CLIENT.getAccountInfo(stake);
const mintInfoBefore = await SRM_TOKEN_CLIENT.getMintInfo();
await program.methods
.distribute()
.accounts({
officer,
treasury,
stake,
srmVault,
srmMint: ORDERBOOK_ENV.mintA,
tokenProgram: TOKEN_PID,
dexProgram: DEX_PID,
})
.rpc();
const srmVaultAfter = await SRM_TOKEN_CLIENT.getAccountInfo(srmVault);
const treasuryAfter = await SRM_TOKEN_CLIENT.getAccountInfo(treasury);
const stakeAfter = await SRM_TOKEN_CLIENT.getAccountInfo(stake);
const mintInfoAfter = await SRM_TOKEN_CLIENT.getMintInfo();
const beforeAmount = 1152000000;
assert.strictEqual(srmVaultBefore.amount.toNumber(), beforeAmount);
assert.strictEqual(srmVaultAfter.amount.toNumber(), 0); // Fully distributed.
assert.strictEqual(
stakeAfter.amount.toNumber(),
beforeAmount * (distribution.stake / 100.0)
);
assert.strictEqual(
treasuryAfter.amount.toNumber(),
beforeAmount * (distribution.treasury / 100.0)
);
// Check burn amount.
assert.strictEqual(mintInfoBefore.supply.toString(), "1000000000000000000");
assert.strictEqual(
mintInfoBefore.supply.sub(mintInfoAfter.supply).toNumber(),
beforeAmount * (distribution.burn / 100.0)
);
});
});

View File

@ -1,599 +0,0 @@
// Boilerplate utils to bootstrap an orderbook for testing on a localnet.
// not super relevant to the point of the example, though may be useful to
// include into your own workspace for testing.
//
// TODO: Modernize all these apis. This is all quite clunky.
const Token = require("@solana/spl-token").Token;
const TOKEN_PROGRAM_ID = require("@solana/spl-token").TOKEN_PROGRAM_ID;
const TokenInstructions = require("@project-serum/serum").TokenInstructions;
const { Market, OpenOrders } = require("@project-serum/serum");
const DexInstructions = require("@project-serum/serum").DexInstructions;
const web3 = require("@project-serum/anchor").web3;
const Connection = web3.Connection;
const anchor = require("@project-serum/anchor");
const BN = anchor.BN;
const serumCmn = require("@project-serum/common");
const Account = web3.Account;
const Transaction = web3.Transaction;
const PublicKey = web3.PublicKey;
const SystemProgram = web3.SystemProgram;
const DEX_PID = new PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin");
const secret = JSON.parse(
require("fs").readFileSync("./scripts/market-maker.json")
);
const MARKET_MAKER = new Account(secret);
async function initMarket({ provider }) {
// Setup mints with initial tokens owned by the provider.
const decimals = 6;
const [MINT_A, GOD_A] = await serumCmn.createMintAndVault(
provider,
new BN("1000000000000000000"),
undefined,
decimals
);
const [MINT_B, GOD_B] = await serumCmn.createMintAndVault(
provider,
new BN("1000000000000000000"),
undefined,
decimals
);
const [USDC, GOD_USDC] = await serumCmn.createMintAndVault(
provider,
new BN("1000000000000000000"),
undefined,
decimals
);
// Create a funded account to act as market maker.
const amount = new BN("10000000000000").muln(10 ** decimals);
const marketMaker = await fundAccount({
provider,
mints: [
{ god: GOD_A, mint: MINT_A, amount, decimals },
{ god: GOD_B, mint: MINT_B, amount, decimals },
{ god: GOD_USDC, mint: USDC, amount, decimals },
],
});
// Setup A/USDC with resting orders.
const asks = [
[6.041, 7.8],
[6.051, 72.3],
[6.055, 5.4],
[6.067, 15.7],
[6.077, 390.0],
[6.09, 24.0],
[6.11, 36.3],
[6.133, 300.0],
[6.167, 687.8],
];
const bids = [
[6.004, 8.5],
[5.995, 12.9],
[5.987, 6.2],
[5.978, 15.3],
[5.965, 82.8],
[5.961, 25.4],
];
[MARKET_A_USDC, marketAVaultSigner] = await setupMarket({
baseMint: MINT_A,
quoteMint: USDC,
marketMaker: {
account: marketMaker.account,
baseToken: marketMaker.tokens[MINT_A.toString()],
quoteToken: marketMaker.tokens[USDC.toString()],
},
bids,
asks,
provider,
});
[MARKET_B_USDC, marketBVaultSigner] = await setupMarket({
baseMint: MINT_B,
quoteMint: USDC,
marketMaker: {
account: marketMaker.account,
baseToken: marketMaker.tokens[MINT_B.toString()],
quoteToken: marketMaker.tokens[USDC.toString()],
},
bids,
asks,
provider,
});
return {
marketA: MARKET_A_USDC,
marketAVaultSigner,
marketB: MARKET_B_USDC,
marketBVaultSigner,
marketMaker,
mintA: MINT_A,
mintB: MINT_B,
usdc: USDC,
godA: GOD_A,
godB: GOD_B,
godUsdc: GOD_USDC,
};
}
async function fundAccount({ provider, mints }) {
const marketMaker = {
tokens: {},
account: MARKET_MAKER,
};
// Transfer lamports to market maker.
await provider.sendAndConfirm(
(() => {
const tx = new Transaction();
tx.add(
SystemProgram.transfer({
fromPubkey: provider.wallet.publicKey,
toPubkey: MARKET_MAKER.publicKey,
lamports: 100000000000,
})
);
return tx;
})()
);
// Transfer SPL tokens to the market maker.
for (let k = 0; k < mints.length; k += 1) {
const { mint, god, amount, decimals } = mints[k];
let MINT_A = mint;
let GOD_A = god;
// Setup token accounts owned by the market maker.
const mintAClient = new Token(
provider.connection,
MINT_A,
TOKEN_PROGRAM_ID,
provider.wallet.payer // node only
);
const marketMakerTokenA = await mintAClient.createAccount(
MARKET_MAKER.publicKey
);
await provider.sendAndConfirm(
(() => {
const tx = new Transaction();
tx.add(
Token.createTransferCheckedInstruction(
TOKEN_PROGRAM_ID,
GOD_A,
MINT_A,
marketMakerTokenA,
provider.wallet.publicKey,
[],
amount,
decimals
)
);
return tx;
})()
);
marketMaker.tokens[mint.toString()] = marketMakerTokenA;
}
return marketMaker;
}
async function setupMarket({
provider,
marketMaker,
baseMint,
quoteMint,
bids,
asks,
}) {
const [marketAPublicKey, vaultOwner] = await listMarket({
connection: provider.connection,
wallet: provider.wallet,
baseMint: baseMint,
quoteMint: quoteMint,
baseLotSize: 100000,
quoteLotSize: 100,
dexProgramId: DEX_PID,
feeRateBps: 0,
});
const MARKET_A_USDC = await Market.load(
provider.connection,
marketAPublicKey,
{ commitment: "processed" },
DEX_PID
);
for (let k = 0; k < asks.length; k += 1) {
let ask = asks[k];
const { transaction, signers } =
await MARKET_A_USDC.makePlaceOrderTransaction(provider.connection, {
owner: marketMaker.account,
payer: marketMaker.baseToken,
side: "sell",
price: ask[0],
size: ask[1],
orderType: "postOnly",
clientId: undefined,
openOrdersAddressKey: undefined,
openOrdersAccount: undefined,
feeDiscountPubkey: null,
selfTradeBehavior: "abortTransaction",
});
await provider.sendAndConfirm(
transaction,
signers.concat(marketMaker.account)
);
}
for (let k = 0; k < bids.length; k += 1) {
let bid = bids[k];
const { transaction, signers } =
await MARKET_A_USDC.makePlaceOrderTransaction(provider.connection, {
owner: marketMaker.account,
payer: marketMaker.quoteToken,
side: "buy",
price: bid[0],
size: bid[1],
orderType: "postOnly",
clientId: undefined,
openOrdersAddressKey: undefined,
openOrdersAccount: undefined,
feeDiscountPubkey: null,
selfTradeBehavior: "abortTransaction",
});
await provider.sendAndConfirm(
transaction,
signers.concat(marketMaker.account)
);
}
return [MARKET_A_USDC, vaultOwner];
}
async function listMarket({
connection,
wallet,
baseMint,
quoteMint,
baseLotSize,
quoteLotSize,
dexProgramId,
feeRateBps,
}) {
const market = new Account();
const requestQueue = new Account();
const eventQueue = new Account();
const bids = new Account();
const asks = new Account();
const baseVault = new Account();
const quoteVault = new Account();
const quoteDustThreshold = new BN(100);
const [vaultOwner, vaultSignerNonce] = await getVaultOwnerAndNonce(
market.publicKey,
dexProgramId
);
const tx1 = new Transaction();
tx1.add(
SystemProgram.createAccount({
fromPubkey: wallet.publicKey,
newAccountPubkey: baseVault.publicKey,
lamports: await connection.getMinimumBalanceForRentExemption(165),
space: 165,
programId: TOKEN_PROGRAM_ID,
}),
SystemProgram.createAccount({
fromPubkey: wallet.publicKey,
newAccountPubkey: quoteVault.publicKey,
lamports: await connection.getMinimumBalanceForRentExemption(165),
space: 165,
programId: TOKEN_PROGRAM_ID,
}),
TokenInstructions.initializeAccount({
account: baseVault.publicKey,
mint: baseMint,
owner: vaultOwner,
}),
TokenInstructions.initializeAccount({
account: quoteVault.publicKey,
mint: quoteMint,
owner: vaultOwner,
})
);
const tx2 = new Transaction();
tx2.add(
SystemProgram.createAccount({
fromPubkey: wallet.publicKey,
newAccountPubkey: market.publicKey,
lamports: await connection.getMinimumBalanceForRentExemption(
Market.getLayout(dexProgramId).span
),
space: Market.getLayout(dexProgramId).span,
programId: dexProgramId,
}),
SystemProgram.createAccount({
fromPubkey: wallet.publicKey,
newAccountPubkey: requestQueue.publicKey,
lamports: await connection.getMinimumBalanceForRentExemption(5120 + 12),
space: 5120 + 12,
programId: dexProgramId,
}),
SystemProgram.createAccount({
fromPubkey: wallet.publicKey,
newAccountPubkey: eventQueue.publicKey,
lamports: await connection.getMinimumBalanceForRentExemption(262144 + 12),
space: 262144 + 12,
programId: dexProgramId,
}),
SystemProgram.createAccount({
fromPubkey: wallet.publicKey,
newAccountPubkey: bids.publicKey,
lamports: await connection.getMinimumBalanceForRentExemption(65536 + 12),
space: 65536 + 12,
programId: dexProgramId,
}),
SystemProgram.createAccount({
fromPubkey: wallet.publicKey,
newAccountPubkey: asks.publicKey,
lamports: await connection.getMinimumBalanceForRentExemption(65536 + 12),
space: 65536 + 12,
programId: dexProgramId,
}),
DexInstructions.initializeMarket({
market: market.publicKey,
requestQueue: requestQueue.publicKey,
eventQueue: eventQueue.publicKey,
bids: bids.publicKey,
asks: asks.publicKey,
baseVault: baseVault.publicKey,
quoteVault: quoteVault.publicKey,
baseMint,
quoteMint,
baseLotSize: new BN(baseLotSize),
quoteLotSize: new BN(quoteLotSize),
feeRateBps,
vaultSignerNonce,
quoteDustThreshold,
programId: dexProgramId,
})
);
const signedTransactions = await signTransactions({
transactionsAndSigners: [
{ transaction: tx1, signers: [baseVault, quoteVault] },
{
transaction: tx2,
signers: [market, requestQueue, eventQueue, bids, asks],
},
],
wallet,
connection,
});
for (let signedTransaction of signedTransactions) {
await sendAndConfirmRawTransaction(
connection,
signedTransaction.serialize()
);
}
const acc = await connection.getAccountInfo(market.publicKey);
return [market.publicKey, vaultOwner];
}
async function signTransactions({
transactionsAndSigners,
wallet,
connection,
}) {
const blockhash = (await connection.getRecentBlockhash("finalized"))
.blockhash;
transactionsAndSigners.forEach(({ transaction, signers = [] }) => {
transaction.recentBlockhash = blockhash;
transaction.setSigners(
wallet.publicKey,
...signers.map((s) => s.publicKey)
);
if (signers?.length > 0) {
transaction.partialSign(...signers);
}
});
return await wallet.signAllTransactions(
transactionsAndSigners.map(({ transaction }) => transaction)
);
}
async function sendAndConfirmRawTransaction(
connection,
raw,
commitment = "processed"
) {
let tx = await connection.sendRawTransaction(raw, {
skipPreflight: true,
});
return await connection.confirmTransaction(tx, commitment);
}
async function getVaultOwnerAndNonce(marketPublicKey, dexProgramId = DEX_PID) {
const nonce = new BN(0);
while (nonce.toNumber() < 255) {
try {
const vaultOwner = await PublicKey.createProgramAddress(
[marketPublicKey.toBuffer(), nonce.toArrayLike(Buffer, "le", 8)],
dexProgramId
);
return [vaultOwner, nonce];
} catch (e) {
nonce.iaddn(1);
}
}
throw new Error("Unable to find nonce");
}
async function runTradeBot(market, provider, iterations = undefined) {
let marketClient = await Market.load(
provider.connection,
market,
{ commitment: "processed" },
DEX_PID
);
const baseTokenUser1 = (
await marketClient.getTokenAccountsByOwnerForMint(
provider.connection,
MARKET_MAKER.publicKey,
marketClient.baseMintAddress
)
)[0].pubkey;
const quoteTokenUser1 = (
await marketClient.getTokenAccountsByOwnerForMint(
provider.connection,
MARKET_MAKER.publicKey,
marketClient.quoteMintAddress
)
)[0].pubkey;
const baseTokenUser2 = (
await marketClient.getTokenAccountsByOwnerForMint(
provider.connection,
provider.wallet.publicKey,
marketClient.baseMintAddress
)
)[0].pubkey;
const quoteTokenUser2 = (
await marketClient.getTokenAccountsByOwnerForMint(
provider.connection,
provider.wallet.publicKey,
marketClient.quoteMintAddress
)
)[0].pubkey;
const makerOpenOrdersUser1 = (
await OpenOrders.findForMarketAndOwner(
provider.connection,
market,
MARKET_MAKER.publicKey,
DEX_PID
)
)[0];
makerOpenOrdersUser2 = (
await OpenOrders.findForMarketAndOwner(
provider.connection,
market,
provider.wallet.publicKey,
DEX_PID
)
)[0];
const price = 6.041;
const size = 700000.8;
let maker = MARKET_MAKER;
let taker = provider.wallet.payer;
let baseToken = baseTokenUser1;
let quoteToken = quoteTokenUser2;
let makerOpenOrders = makerOpenOrdersUser1;
let k = 1;
while (true) {
if (iterations && k > iterations) {
break;
}
const clientId = new anchor.BN(k);
if (k % 5 === 0) {
if (maker.publicKey.equals(MARKET_MAKER.publicKey)) {
maker = provider.wallet.payer;
makerOpenOrders = makerOpenOrdersUser2;
taker = MARKET_MAKER;
baseToken = baseTokenUser2;
quoteToken = quoteTokenUser1;
} else {
maker = MARKET_MAKER;
makerOpenOrders = makerOpenOrdersUser1;
taker = provider.wallet.payer;
baseToken = baseTokenUser1;
quoteToken = quoteTokenUser2;
}
}
// Post ask.
const { transaction: tx_ask, signers: sigs_ask } =
await marketClient.makePlaceOrderTransaction(provider.connection, {
owner: maker,
payer: baseToken,
side: "sell",
price,
size,
orderType: "postOnly",
clientId,
openOrdersAddressKey: undefined,
openOrdersAccount: undefined,
feeDiscountPubkey: null,
selfTradeBehavior: "abortTransaction",
});
let txSig = await provider.sendAndConfirm(tx_ask, sigs_ask.concat(maker));
console.log("Ask", txSig);
// Take.
const { transaction: tx_bid, signers: sigs_bid } =
await marketClient.makePlaceOrderTransaction(provider.connection, {
owner: taker,
payer: quoteToken,
side: "buy",
price,
size,
orderType: "ioc",
clientId: undefined,
openOrdersAddressKey: undefined,
openOrdersAccount: undefined,
feeDiscountPubkey: null,
selfTradeBehavior: "abortTransaction",
});
txSig = await provider.sendAndConfirm(tx_bid, sigs_bid.concat(taker));
console.log("Bid", txSig);
await sleep(1000);
// Cancel anything remaining.
try {
txSig = await marketClient.cancelOrderByClientId(
provider.connection,
maker,
makerOpenOrders.address,
clientId
);
console.log("Cancelled the rest", txSig);
await sleep(1000);
} catch (e) {
console.log("Unable to cancel order", e);
}
k += 1;
// If the open orders account wasn't previously initialized, it is now.
if (makerOpenOrdersUser2 === undefined) {
makerOpenOrdersUser2 = (
await OpenOrders.findForMarketAndOwner(
provider.connection,
market,
provider.wallet.publicKey,
DEX_PID
)
)[0];
}
}
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
module.exports = {
fundAccount,
initMarket,
setupMarket,
DEX_PID,
getVaultOwnerAndNonce,
runTradeBot,
};

View File

@ -1,187 +0,0 @@
const anchor = require("@project-serum/anchor");
const serumCmn = require("@project-serum/common");
const TokenInstructions = require("@project-serum/serum").TokenInstructions;
const utils = require("../../deps/stake/tests/utils");
const lockup = anchor.workspace.Lockup;
const registry = anchor.workspace.Registry;
const provider = anchor.AnchorProvider.env();
// hack so we don't have to update serum-common library
// to the new AnchorProvider class and Provider interface
provider.send = provider.sendAndConfirm;
let lockupAddress = null;
let mint = null;
let god = null;
let registrarAccount = null;
let registrarSigner = null;
let nonce = null;
let poolMint = null;
const registrar = new anchor.web3.Account();
const rewardQ = new anchor.web3.Account();
const withdrawalTimelock = new anchor.BN(4);
const stakeRate = new anchor.BN(2);
const rewardQLen = 170;
let member = null;
let memberAccount = null;
let memberSigner = null;
let balances = null;
let balancesLocked = null;
const WHITELIST_SIZE = 10;
async function setupStakePool(mint, god) {
// Registry genesis.
const [_registrarSigner, _nonce] =
await anchor.web3.PublicKey.findProgramAddress(
[registrar.publicKey.toBuffer()],
registry.programId
);
registrarSigner = _registrarSigner;
nonce = _nonce;
poolMint = await serumCmn.createMint(provider, registrarSigner);
try {
// Init registry.
await registry.state.rpc.new({
accounts: { lockupProgram: lockup.programId },
});
// Init lockup.
await lockup.state.rpc.new({
accounts: {
authority: provider.wallet.publicKey,
},
});
} catch (err) {
// Skip errors for convenience when developing locally,
// since the state constructors can only be called once.
}
// Initialize stake pool.
await registry.rpc.initialize(
mint,
provider.wallet.publicKey,
nonce,
withdrawalTimelock,
stakeRate,
rewardQLen,
{
accounts: {
registrar: registrar.publicKey,
poolMint,
rewardEventQ: rewardQ.publicKey,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
},
signers: [registrar, rewardQ],
instructions: [
await registry.account.registrar.createInstruction(registrar),
await registry.account.rewardQueue.createInstruction(rewardQ, 8250),
],
}
);
registrarAccount = await registry.account.registrar.fetch(
registrar.publicKey
);
console.log("Registrar", registrar.publicKey.toString());
console.log("Wallet", registry.provider.wallet.publicKey.toString());
// Create account for staker.
const seed = anchor.utils.sha256
.hash(`${registrar.publicKey.toString()}:Member`)
.slice(0, 32);
member = await anchor.web3.PublicKey.createWithSeed(
registry.provider.wallet.publicKey,
seed,
registry.programId
);
const [_memberSigner, nonce2] =
await anchor.web3.PublicKey.findProgramAddress(
[registrar.publicKey.toBuffer(), member.toBuffer()],
registry.programId
);
memberSigner = _memberSigner;
const [mainTx, _balances] = await utils.createBalanceSandbox(
provider,
registrarAccount,
memberSigner
);
const [lockedTx, _balancesLocked] = await utils.createBalanceSandbox(
provider,
registrarAccount,
memberSigner
);
balances = _balances;
balancesLocked = _balancesLocked;
const tx = registry.transaction.createMember(nonce2, {
accounts: {
registrar: registrar.publicKey,
member: member,
beneficiary: provider.wallet.publicKey,
memberSigner,
balances,
balancesLocked,
tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
},
instructions: [
anchor.web3.SystemProgram.createAccountWithSeed({
fromPubkey: registry.provider.wallet.publicKey,
newAccountPubkey: member,
basePubkey: registry.provider.wallet.publicKey,
seed,
lamports:
await registry.provider.connection.getMinimumBalanceForRentExemption(
registry.account.member.size
),
space: registry.account.member.size,
programId: registry.programId,
}),
],
});
const signers = [provider.wallet.payer];
const allTxs = [mainTx, lockedTx, { tx, signers }];
await provider.sendAll(allTxs);
memberAccount = await registry.account.member.fetch(member);
// Deposit into stake program.
const depositAmount = new anchor.BN(120);
await registry.rpc.deposit(depositAmount, {
accounts: {
depositor: god,
depositorAuthority: provider.wallet.publicKey,
tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
vault: memberAccount.balances.vault,
beneficiary: provider.wallet.publicKey,
member: member,
},
});
// Stake.
const stakeAmount = new anchor.BN(10);
await registry.rpc.stake(stakeAmount, false, {
accounts: {
// Stake instance.
registrar: registrar.publicKey,
rewardEventQ: rewardQ.publicKey,
poolMint,
// Member.
member: member,
beneficiary: provider.wallet.publicKey,
balances,
balancesLocked,
// Program signers.
memberSigner,
registrarSigner,
// Misc.
clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID,
},
});
}
module.exports = {
setupStakePool,
};

View File

@ -1,11 +0,0 @@
[provider]
cluster = "localnet"
wallet = "~/.config/solana/id.json"
[programs.localnet]
chat = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
[scripts]
test = "yarn run mocha -t 1000000 tests/"
[features]

View File

@ -1,4 +0,0 @@
[workspace]
members = [
"programs/*"
]

View File

@ -1,12 +0,0 @@
// Migrations are an early feature. Currently, they're nothing more than this
// single deploy script that's invoked from the CLI, injecting a provider
// configured from the workspace's Anchor.toml.
const anchor = require("@project-serum/anchor");
module.exports = async function (provider) {
// Configure client to use the provider.
anchor.setProvider(provider);
// Add your deploy script here.
};

View File

@ -1,19 +0,0 @@
{
"name": "chat",
"version": "0.25.0",
"license": "(MIT OR Apache-2.0)",
"homepage": "https://github.com/coral-xyz/anchor#readme",
"bugs": {
"url": "https://github.com/coral-xyz/anchor/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/coral-xyz/anchor.git"
},
"engines": {
"node": ">=11"
},
"scripts": {
"test": "anchor test"
}
}

View File

@ -1,19 +0,0 @@
[package]
name = "chat"
version = "0.1.0"
description = "Created with Anchor"
rust-version = "1.56"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
name = "chat"
[features]
no-entrypoint = []
no-idl = []
cpi = ["no-entrypoint"]
default = []
[dependencies]
anchor-lang = { path = "../../../../lang" }

View File

@ -1,2 +0,0 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -1,113 +0,0 @@
//! A simple chat program using a ring buffer to store messages.
use anchor_lang::accounts::loader::Loader;
use anchor_lang::prelude::*;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
pub mod chat {
use super::*;
pub fn create_user(ctx: Context<CreateUser>, name: String) -> Result<()> {
ctx.accounts.user.name = name;
ctx.accounts.user.authority = *ctx.accounts.authority.key;
ctx.accounts.user.bump = *ctx.bumps.get("user").unwrap();
Ok(())
}
pub fn create_chat_room(ctx: Context<CreateChatRoom>, name: String) -> Result<()> {
let given_name = name.as_bytes();
let mut name = [0u8; 280];
name[..given_name.len()].copy_from_slice(given_name);
let mut chat = ctx.accounts.chat_room.load_init()?;
chat.name = name;
Ok(())
}
pub fn send_message(ctx: Context<SendMessage>, msg: String) -> Result<()> {
let mut chat = ctx.accounts.chat_room.load_mut()?;
chat.append({
let src = msg.as_bytes();
let mut data = [0u8; 280];
data[..src.len()].copy_from_slice(src);
Message {
from: *ctx.accounts.user.to_account_info().key,
data,
}
});
Ok(())
}
}
#[derive(Accounts)]
#[instruction(name: String)]
pub struct CreateUser<'info> {
#[account(
init,
seeds = [authority.key().as_ref()],
bump,
payer = authority,
space = 320,
)]
user: Account<'info, User>,
#[account(mut)]
authority: Signer<'info>,
system_program: AccountInfo<'info>,
}
#[derive(Accounts)]
pub struct CreateChatRoom<'info> {
#[account(zero)]
chat_room: Loader<'info, ChatRoom>,
}
#[derive(Accounts)]
pub struct SendMessage<'info> {
#[account(
seeds = [authority.key().as_ref()],
bump = user.bump,
has_one = authority,
)]
user: Account<'info, User>,
authority: Signer<'info>,
#[account(mut)]
chat_room: Loader<'info, ChatRoom>,
}
#[account]
pub struct User {
name: String,
authority: Pubkey,
bump: u8,
}
#[account(zero_copy)]
pub struct ChatRoom {
head: u64,
tail: u64,
name: [u8; 280], // Human readable name (char bytes).
messages: [Message; 33607], // Leaves the account at 10,485,680 bytes.
}
impl ChatRoom {
fn append(&mut self, msg: Message) {
self.messages[ChatRoom::index_of(self.head)] = msg;
if ChatRoom::index_of(self.head + 1) == ChatRoom::index_of(self.tail) {
self.tail += 1;
}
self.head += 1;
}
fn index_of(counter: u64) -> usize {
std::convert::TryInto::try_into(counter % 33607).unwrap()
}
}
#[zero_copy]
pub struct Message {
pub from: Pubkey,
pub data: [u8; 280],
}
#[error_code]
pub enum ErrorCode {
Unknown,
}

View File

@ -1,106 +0,0 @@
const anchor = require("@project-serum/anchor");
const { assert } = require("chai");
const { PublicKey } = anchor.web3;
describe("chat", () => {
// Configure the client to use the local cluster.
anchor.setProvider(anchor.AnchorProvider.env());
// Program client handle.
const program = anchor.workspace.Chat;
// Chat room account.
const chatRoom = anchor.web3.Keypair.generate();
it("Creates a chat room", async () => {
await program.rpc.createChatRoom("Test Chat", {
accounts: {
chatRoom: chatRoom.publicKey,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
},
instructions: [
await program.account.chatRoom.createInstruction(chatRoom),
],
signers: [chatRoom],
});
const chat = await program.account.chatRoom.fetch(chatRoom.publicKey);
const name = new TextDecoder("utf-8").decode(new Uint8Array(chat.name));
assert.isTrue(name.startsWith("Test Chat")); // [u8; 280] => trailing zeros.
assert.lengthOf(chat.messages, 33607);
assert.strictEqual(chat.head.toNumber(), 0);
assert.strictEqual(chat.tail.toNumber(), 0);
});
it("Creates a user", async () => {
const authority = program.provider.wallet.publicKey;
const [user, bump] = await PublicKey.findProgramAddress(
[authority.toBuffer()],
program.programId
);
await program.rpc.createUser("My User", {
accounts: {
user,
authority,
systemProgram: anchor.web3.SystemProgram.programId,
},
});
const account = await program.account.user.fetch(user);
assert.strictEqual(account.name, "My User");
assert.isTrue(account.authority.equals(authority));
});
it("Sends messages", async () => {
const authority = program.provider.wallet.publicKey;
const user = (
await PublicKey.findProgramAddress(
[authority.toBuffer()],
program.programId
)
)[0];
// Only send a couple messages so the test doesn't take an eternity.
const numMessages = 10;
// Generate random message strings.
const messages = new Array(numMessages).fill("").map((msg) => {
return (
Math.random().toString(36).substring(2, 15) +
Math.random().toString(36).substring(2, 15)
);
});
// Send each message.
for (let k = 0; k < numMessages; k += 1) {
console.log("Sending message " + k);
await program.rpc.sendMessage(messages[k], {
accounts: {
user,
authority,
chatRoom: chatRoom.publicKey,
},
});
}
// Check the chat room state is as expected.
const chat = await program.account.chatRoom.fetch(chatRoom.publicKey);
const name = new TextDecoder("utf-8").decode(new Uint8Array(chat.name));
assert.isTrue(name.startsWith("Test Chat")); // [u8; 280] => trailing zeros.
assert.lengthOf(chat.messages, 33607);
assert.strictEqual(chat.head.toNumber(), numMessages);
assert.strictEqual(chat.tail.toNumber(), 0);
chat.messages.forEach((msg, idx) => {
if (idx < 10) {
const data = new TextDecoder("utf-8").decode(new Uint8Array(msg.data));
console.log("Message", data);
assert.isTrue(msg.from.equals(user));
assert.isTrue(data.startsWith(messages[idx]));
} else {
assert.strictEqual(
JSON.stringify(msg.data),
JSON.stringify(new Array(280).fill(0))
);
}
});
});
});

View File

@ -1,9 +0,0 @@
[provider]
cluster = "localnet"
wallet = "~/.config/solana/id.json"
[programs.localnet]
composite = "EHthziFziNoac9LBGxEaVN47Y3uUiRoXvqAiR6oes4iU"
[scripts]
test = "yarn run mocha -t 1000000 tests/"

View File

@ -1,4 +0,0 @@
[workspace]
members = [
"programs/*"
]

View File

@ -1,19 +0,0 @@
{
"name": "composite",
"version": "0.25.0",
"license": "(MIT OR Apache-2.0)",
"homepage": "https://github.com/coral-xyz/anchor#readme",
"bugs": {
"url": "https://github.com/coral-xyz/anchor/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/coral-xyz/anchor.git"
},
"engines": {
"node": ">=11"
},
"scripts": {
"test": "anchor test"
}
}

View File

@ -1,17 +0,0 @@
[package]
name = "composite"
version = "0.1.0"
description = "Created with Anchor"
rust-version = "1.56"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
name = "composite"
[features]
no-entrypoint = []
cpi = ["no-entrypoint"]
[dependencies]
anchor-lang = { path = "../../../../lang" }

View File

@ -1,2 +0,0 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -1,64 +0,0 @@
//! This example demonstrates the ability to compose together multiple
//! structs deriving `Accounts`. See `CompositeUpdate`, below.
use anchor_lang::prelude::*;
declare_id!("EHthziFziNoac9LBGxEaVN47Y3uUiRoXvqAiR6oes4iU");
#[program]
mod composite {
use super::*;
pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
Ok(())
}
pub fn composite_update(
ctx: Context<CompositeUpdate>,
dummy_a: u64,
dummy_b: u64,
) -> Result<()> {
let a = &mut ctx.accounts.foo.dummy_a;
let b = &mut ctx.accounts.bar.dummy_b;
a.data = dummy_a;
b.data = dummy_b;
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(zero)]
pub dummy_a: Account<'info, DummyA>,
#[account(zero)]
pub dummy_b: Account<'info, DummyB>,
}
#[derive(Accounts)]
pub struct CompositeUpdate<'info> {
foo: Foo<'info>,
bar: Bar<'info>,
}
#[derive(Accounts)]
pub struct Foo<'info> {
#[account(mut)]
pub dummy_a: Account<'info, DummyA>,
}
#[derive(Accounts)]
pub struct Bar<'info> {
#[account(mut)]
pub dummy_b: Account<'info, DummyB>,
}
#[account]
pub struct DummyA {
pub data: u64,
}
#[account]
pub struct DummyB {
pub data: u64,
}

View File

@ -1,50 +0,0 @@
const { assert } = require("chai");
const anchor = require("@project-serum/anchor");
describe("composite", () => {
const provider = anchor.AnchorProvider.local();
// Configure the client to use the local cluster.
anchor.setProvider(provider);
it("Is initialized!", async () => {
const program = anchor.workspace.Composite;
const dummyA = anchor.web3.Keypair.generate();
const dummyB = anchor.web3.Keypair.generate();
const tx = await program.rpc.initialize({
accounts: {
dummyA: dummyA.publicKey,
dummyB: dummyB.publicKey,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
},
signers: [dummyA, dummyB],
instructions: [
await program.account.dummyA.createInstruction(dummyA),
await program.account.dummyB.createInstruction(dummyB),
],
});
await program.rpc.compositeUpdate(
new anchor.BN(1234),
new anchor.BN(4321),
{
accounts: {
foo: {
dummyA: dummyA.publicKey,
},
bar: {
dummyB: dummyB.publicKey,
},
},
}
);
const dummyAAccount = await program.account.dummyA.fetch(dummyA.publicKey);
const dummyBAccount = await program.account.dummyB.fetch(dummyB.publicKey);
assert.isTrue(dummyAAccount.data.eq(new anchor.BN(1234)));
assert.isTrue(dummyBAccount.data.eq(new anchor.BN(4321)));
});
});

View File

@ -1,6 +0,0 @@
.anchor
.DS_Store
target
**/*.rs.bk
node_modules

View File

@ -1,16 +0,0 @@
[features]
seeds = false
[programs.localnet]
callee = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
caller = "HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L"
[registry]
url = "https://anchor.projectserum.com"
[provider]
cluster = "localnet"
wallet = "~/.config/solana/id.json"
[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

View File

@ -1,4 +0,0 @@
[workspace]
members = [
"programs/*"
]

View File

@ -1,12 +0,0 @@
// Migrations are an early feature. Currently, they're nothing more than this
// single deploy script that's invoked from the CLI, injecting a provider
// configured from the workspace's Anchor.toml.
const anchor = require("@project-serum/anchor");
module.exports = async function (provider) {
// Configure client to use the provider.
anchor.setProvider(provider);
// Add your deploy script here.
};

View File

@ -1,19 +0,0 @@
{
"name": "cpi-returns",
"version": "0.25.0",
"license": "(MIT OR Apache-2.0)",
"homepage": "https://github.com/coral-xyz/anchor#readme",
"bugs": {
"url": "https://github.com/coral-xyz/anchor/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/coral-xyz/anchor.git"
},
"engines": {
"node": ">=11"
},
"scripts": {
"test": "anchor run test-with-build"
}
}

View File

@ -1,19 +0,0 @@
[package]
name = "callee"
version = "0.1.0"
description = "Created with Anchor"
edition = "2018"
[lib]
crate-type = ["cdylib", "lib"]
name = "callee"
[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []
[dependencies]
anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] }

View File

@ -1,2 +0,0 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -1,57 +0,0 @@
use anchor_lang::prelude::*;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
pub mod callee {
use super::*;
#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct StructReturn {
pub value: u64,
}
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
let account = &mut ctx.accounts.account;
account.value = 10;
Ok(())
}
pub fn return_u64(_ctx: Context<CpiReturn>) -> Result<u64> {
Ok(10)
}
pub fn return_struct(_ctx: Context<CpiReturn>) -> Result<StructReturn> {
let s = StructReturn { value: 11 };
Ok(s)
}
pub fn return_vec(_ctx: Context<CpiReturn>) -> Result<Vec<u8>> {
Ok(vec![12, 13, 14, 100])
}
// Used for testing views
pub fn return_u64_from_account(ctx: Context<CpiReturn>) -> Result<u64> {
let account = &ctx.accounts.account;
Ok(account.value)
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = user, space = 8 + 8)]
pub account: Account<'info, CpiReturnAccount>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct CpiReturn<'info> {
pub account: Account<'info, CpiReturnAccount>,
}
#[account]
pub struct CpiReturnAccount {
pub value: u64,
}

View File

@ -1,20 +0,0 @@
[package]
name = "caller"
version = "0.1.0"
description = "Created with Anchor"
edition = "2018"
[lib]
crate-type = ["cdylib", "lib"]
name = "caller"
[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []
[dependencies]
anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] }
callee = { path = "../callee", features = ["cpi"] }

View File

@ -1,2 +0,0 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -1,75 +0,0 @@
use anchor_lang::prelude::*;
use callee::cpi::accounts::CpiReturn;
use callee::program::Callee;
use callee::{self, CpiReturnAccount};
declare_id!("HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L");
#[program]
pub mod caller {
use super::*;
#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct Struct {
pub a: u64,
pub b: u64,
}
pub fn cpi_call_return_u64(ctx: Context<CpiReturnContext>) -> Result<()> {
let cpi_program = ctx.accounts.cpi_return_program.to_account_info();
let cpi_accounts = CpiReturn {
account: ctx.accounts.cpi_return.to_account_info(),
};
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
let result = callee::cpi::return_u64(cpi_ctx)?;
let solana_return = result.get();
anchor_lang::solana_program::log::sol_log_data(&[&solana_return.try_to_vec().unwrap()]);
Ok(())
}
pub fn cpi_call_return_struct(ctx: Context<CpiReturnContext>) -> Result<()> {
let cpi_program = ctx.accounts.cpi_return_program.to_account_info();
let cpi_accounts = CpiReturn {
account: ctx.accounts.cpi_return.to_account_info(),
};
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
let result = callee::cpi::return_struct(cpi_ctx)?;
let solana_return = result.get();
anchor_lang::solana_program::log::sol_log_data(&[&solana_return.try_to_vec().unwrap()]);
Ok(())
}
pub fn cpi_call_return_vec(ctx: Context<CpiReturnContext>) -> Result<()> {
let cpi_program = ctx.accounts.cpi_return_program.to_account_info();
let cpi_accounts = CpiReturn {
account: ctx.accounts.cpi_return.to_account_info(),
};
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
let result = callee::cpi::return_vec(cpi_ctx)?;
let solana_return = result.get();
anchor_lang::solana_program::log::sol_log_data(&[&solana_return.try_to_vec().unwrap()]);
Ok(())
}
pub fn return_u64(ctx: Context<ReturnContext>) -> Result<u64> {
Ok(99)
}
pub fn return_struct(ctx: Context<ReturnContext>) -> Result<Struct> {
Ok(Struct { a: 1, b: 2 })
}
pub fn return_vec(ctx: Context<ReturnContext>) -> Result<Vec<u64>> {
Ok(vec![1, 2, 3])
}
}
#[derive(Accounts)]
pub struct CpiReturnContext<'info> {
#[account(mut)]
pub cpi_return: Account<'info, CpiReturnAccount>,
pub cpi_return_program: Program<'info, Callee>,
}
#[derive(Accounts)]
pub struct ReturnContext {}

View File

@ -1,226 +0,0 @@
import assert from "assert";
import * as anchor from "@project-serum/anchor";
import * as borsh from "borsh";
import { Program } from "@project-serum/anchor";
import { Callee } from "../target/types/callee";
import { Caller } from "../target/types/caller";
import { ConfirmOptions } from "@solana/web3.js";
const { SystemProgram } = anchor.web3;
describe("CPI return", () => {
const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
const callerProgram = anchor.workspace.Caller as Program<Caller>;
const calleeProgram = anchor.workspace.Callee as Program<Callee>;
const getReturnLog = (confirmedTransaction) => {
const prefix = "Program return: ";
let log = confirmedTransaction.meta.logMessages.find((log) =>
log.startsWith(prefix)
);
log = log.slice(prefix.length);
const [key, data] = log.split(" ", 2);
const buffer = Buffer.from(data, "base64");
return [key, data, buffer];
};
const cpiReturn = anchor.web3.Keypair.generate();
const confirmOptions: ConfirmOptions = { commitment: "confirmed" };
it("can initialize", async () => {
await calleeProgram.methods
.initialize()
.accounts({
account: cpiReturn.publicKey,
user: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
})
.signers([cpiReturn])
.rpc();
});
it("can return u64 from a cpi", async () => {
const tx = await callerProgram.methods
.cpiCallReturnU64()
.accounts({
cpiReturn: cpiReturn.publicKey,
cpiReturnProgram: calleeProgram.programId,
})
.rpc(confirmOptions);
let t = await provider.connection.getTransaction(tx, {
commitment: "confirmed",
});
const [key, data, buffer] = getReturnLog(t);
assert.equal(key, calleeProgram.programId);
// Check for matching log on receive side
let receiveLog = t.meta.logMessages.find(
(log) => log == `Program data: ${data}`
);
assert(receiveLog !== undefined);
const reader = new borsh.BinaryReader(buffer);
assert.equal(reader.readU64().toNumber(), 10);
});
it("can make a non-cpi call to a function that returns a u64", async () => {
const tx = await calleeProgram.methods
.returnU64()
.accounts({
account: cpiReturn.publicKey,
})
.rpc(confirmOptions);
let t = await provider.connection.getTransaction(tx, {
commitment: "confirmed",
});
const [key, , buffer] = getReturnLog(t);
assert.equal(key, calleeProgram.programId);
const reader = new borsh.BinaryReader(buffer);
assert.equal(reader.readU64().toNumber(), 10);
});
it("can return a struct from a cpi", async () => {
const tx = await callerProgram.methods
.cpiCallReturnStruct()
.accounts({
cpiReturn: cpiReturn.publicKey,
cpiReturnProgram: calleeProgram.programId,
})
.rpc(confirmOptions);
let t = await provider.connection.getTransaction(tx, {
commitment: "confirmed",
});
const [key, data, buffer] = getReturnLog(t);
assert.equal(key, calleeProgram.programId);
// Check for matching log on receive side
let receiveLog = t.meta.logMessages.find(
(log) => log == `Program data: ${data}`
);
assert(receiveLog !== undefined);
// Deserialize the struct and validate
class Assignable {
constructor(properties) {
Object.keys(properties).map((key) => {
this[key] = properties[key];
});
}
}
class Data extends Assignable {}
const schema = new Map([
[Data, { kind: "struct", fields: [["value", "u64"]] }],
]);
const deserialized = borsh.deserialize(schema, Data, buffer);
assert(deserialized.value.toNumber() === 11);
});
it("can return a vec from a cpi", async () => {
const tx = await callerProgram.methods
.cpiCallReturnVec()
.accounts({
cpiReturn: cpiReturn.publicKey,
cpiReturnProgram: calleeProgram.programId,
})
.rpc(confirmOptions);
let t = await provider.connection.getTransaction(tx, {
commitment: "confirmed",
});
const [key, data, buffer] = getReturnLog(t);
assert.equal(key, calleeProgram.programId);
// Check for matching log on receive side
let receiveLog = t.meta.logMessages.find(
(log) => log == `Program data: ${data}`
);
assert(receiveLog !== undefined);
const reader = new borsh.BinaryReader(buffer);
const array = reader.readArray(() => reader.readU8());
assert.deepStrictEqual(array, [12, 13, 14, 100]);
});
it("sets a return value in idl", async () => {
// @ts-expect-error
const returnu64Instruction = calleeProgram._idl.instructions.find(
(f) => f.name == "returnU64"
);
assert.equal(returnu64Instruction.returns, "u64");
// @ts-expect-error
const returnStructInstruction = calleeProgram._idl.instructions.find(
(f) => f.name == "returnStruct"
);
assert.deepStrictEqual(returnStructInstruction.returns, {
defined: "StructReturn",
});
});
it("can return a u64 via view", async () => {
// @ts-expect-error
assert(new anchor.BN(99).eq(await callerProgram.views.returnU64()));
// Via methods API
assert(
new anchor.BN(99).eq(await callerProgram.methods.returnU64().view())
);
});
it("can return a struct via view", async () => {
// @ts-expect-error
const struct = await callerProgram.views.returnStruct();
assert(struct.a.eq(new anchor.BN(1)));
assert(struct.b.eq(new anchor.BN(2)));
// Via methods API
const struct2 = await callerProgram.methods.returnStruct().view();
assert(struct2.a.eq(new anchor.BN(1)));
assert(struct2.b.eq(new anchor.BN(2)));
});
it("can return a vec via view", async () => {
// @ts-expect-error
const vec = await callerProgram.views.returnVec();
assert(vec[0].eq(new anchor.BN(1)));
assert(vec[1].eq(new anchor.BN(2)));
assert(vec[2].eq(new anchor.BN(3)));
// Via methods API
const vec2 = await callerProgram.methods.returnVec().view();
assert(vec2[0].eq(new anchor.BN(1)));
assert(vec2[1].eq(new anchor.BN(2)));
assert(vec2[2].eq(new anchor.BN(3)));
});
it("can return a u64 from an account via view", async () => {
const value = new anchor.BN(10);
assert(
value.eq(
await calleeProgram.methods
.returnU64FromAccount()
.accounts({ account: cpiReturn.publicKey })
.view()
)
);
});
it("cant call view on mutable instruction", async () => {
assert.equal(calleeProgram.views.initialize, undefined);
try {
await calleeProgram.methods
.initialize()
.accounts({
account: cpiReturn.publicKey,
user: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
})
.signers([cpiReturn])
.view();
} catch (e) {
assert(e.message.includes("Method does not support views"));
}
});
});

View File

@ -1,11 +0,0 @@
{
"compilerOptions": {
"types": ["mocha", "chai"],
"typeRoots": ["./node_modules/@types"],
"lib": ["es2015"],
"module": "commonjs",
"target": "es6",
"esModuleInterop": true,
"skipLibCheck": true
}
}

View File

@ -1,17 +0,0 @@
[programs.localnet]
custom_coder = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
spl_token = "FmpfPa1LHEYRbueNMnwNVd2JvyQ89GXGWdyZEXNNKV8w"
native_system = "9NxAd91hhJ3ZBTHytYP894y4ESRKG7n8VbLgdyYGJFLB"
spl_associated_token = "4dUGnkre6uBhX1abB4ofkoecGN4aDXdiWSaWLUjVw6bh"
[registry]
url = "https://anchor.projectserum.com"
[provider]
cluster = "localnet"
wallet = "~/.config/solana/id.json"
[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
[features]

View File

@ -1,4 +0,0 @@
[workspace]
members = [
"programs/*"
]

View File

@ -1,12 +0,0 @@
// Migrations are an early feature. Currently, they're nothing more than this
// single deploy script that's invoked from the CLI, injecting a provider
// configured from the workspace's Anchor.toml.
const anchor = require("@project-serum/anchor");
module.exports = async function (provider) {
// Configure client to use the provider.
anchor.setProvider(provider);
// Add your deploy script here.
};

View File

@ -1,23 +0,0 @@
{
"name": "custom-coder",
"version": "0.20.0",
"license": "(MIT OR Apache-2.0)",
"homepage": "https://github.com/coral-xyz/anchor#readme",
"bugs": {
"url": "https://github.com/coral-xyz/anchor/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/coral-xyz/anchor.git"
},
"engines": {
"node": ">=11"
},
"scripts": {
"test": "anchor test"
},
"dependencies": {
"mocha": "^10.0.0",
"ts-mocha": "^10.0.0"
}
}

View File

@ -1,20 +0,0 @@
[package]
name = "custom-coder"
version = "0.1.0"
description = "Created with Anchor"
rust-version = "1.56"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
name = "custom_coder"
[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []
[dependencies]
anchor-lang = { path = "../../../../lang" }

View File

@ -1,2 +0,0 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -1,14 +0,0 @@
use anchor_lang::prelude::*;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
pub mod custom_coder {
use super::*;
pub fn initialize(_ctx: Context<Initialize>, a: Option<u8>) -> Result<()> {
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize {}

View File

@ -1,20 +0,0 @@
[package]
name = "native-system"
version = "0.1.0"
description = "Created with Anchor"
rust-version = "1.56"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
name = "native_system"
[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []
[dependencies]
anchor-lang = { path = "../../../../lang" }

View File

@ -1,2 +0,0 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -1,214 +0,0 @@
use anchor_lang::prelude::*;
declare_id!("9NxAd91hhJ3ZBTHytYP894y4ESRKG7n8VbLgdyYGJFLB");
#[program]
pub mod native_system {
use super::*;
pub fn create_account(
ctx: Context<CreateAccount>,
lamports: u64,
space: u64,
owner: Pubkey,
) -> Result<()> {
Ok(())
}
pub fn assign(ctx: Context<Assign>, owner: Pubkey) -> Result<()> {
Ok(())
}
pub fn transfer(ctx: Context<Transfer>, lamports: u64) -> Result<()> {
Ok(())
}
pub fn create_account_with_seed(
ctx: Context<CreateAccountWithSeed>,
base: Pubkey,
seed: String,
lamports: u64,
space: u64,
owner: Pubkey,
) -> Result<()> {
Ok(())
}
pub fn advance_nonce_account(
ctx: Context<AdvanceNonceAccount>,
authorized: Pubkey,
) -> Result<()> {
Ok(())
}
pub fn withdraw_nonce_account(ctx: Context<WithdrawNonceAccount>, lamports: u64) -> Result<()> {
Ok(())
}
pub fn initialize_nonce_account(
ctx: Context<InitializeNonceAccount>,
authorized: Pubkey,
) -> Result<()> {
Ok(())
}
pub fn authorize_nonce_account(
ctx: Context<AuthorizeNonceAccount>,
authorized: Pubkey,
) -> Result<()> {
Ok(())
}
pub fn allocate(ctx: Context<Allocate>, space: u64) -> Result<()> {
Ok(())
}
pub fn allocate_with_seed(
ctx: Context<AllocateWithSeed>,
base: Pubkey,
seed: String,
space: u64,
owner: Pubkey,
) -> Result<()> {
Ok(())
}
pub fn assign_with_seed(
ctx: Context<AssignWithSeed>,
base: Pubkey,
seed: String,
owner: Pubkey,
) -> Result<()> {
Ok(())
}
pub fn transfer_with_seed(
ctx: Context<TransferWithSeed>,
lamports: u64,
seed: String,
owner: Pubkey,
) -> Result<()> {
Ok(())
}
}
#[derive(Accounts)]
pub struct CreateAccount<'info> {
#[account(mut)]
from: Signer<'info>,
#[account(mut)]
to: Signer<'info>,
}
#[derive(Accounts)]
pub struct Assign<'info> {
#[account(mut)]
pubkey: Signer<'info>,
}
#[derive(Accounts)]
pub struct Transfer<'info> {
#[account(mut)]
from: Signer<'info>,
#[account(mut)]
/// CHECK:
to: AccountInfo<'info>,
}
#[derive(Accounts)]
pub struct CreateAccountWithSeed<'info> {
#[account(mut)]
from: Signer<'info>,
#[account(mut)]
/// CHECK:
to: AccountInfo<'info>,
base: Signer<'info>,
}
#[derive(Accounts)]
pub struct AdvanceNonceAccount<'info> {
#[account(mut)]
/// CHECK:
nonce: AccountInfo<'info>,
/// CHECK:
recent_blockhashes: AccountInfo<'info>,
authorized: Signer<'info>,
}
#[derive(Accounts)]
pub struct WithdrawNonceAccount<'info> {
#[account(mut)]
/// CHECK:
nonce: AccountInfo<'info>,
#[account(mut)]
/// CHECK:
to: AccountInfo<'info>,
/// CHECK:
recent_blockhashes: AccountInfo<'info>,
rent: Sysvar<'info, Rent>,
authorized: Signer<'info>,
}
#[derive(Accounts)]
pub struct InitializeNonceAccount<'info> {
#[account(mut)]
nonce: Signer<'info>,
/// CHECK:
recent_blockhashes: AccountInfo<'info>,
rent: Sysvar<'info, Rent>,
}
#[derive(Accounts)]
pub struct AuthorizeNonceAccount<'info> {
#[account(mut)]
/// CHECK:
nonce: AccountInfo<'info>,
authorized: Signer<'info>,
}
#[derive(Accounts)]
pub struct Allocate<'info> {
#[account(mut)]
pubkey: Signer<'info>,
}
#[derive(Accounts)]
pub struct AllocateWithSeed<'info> {
#[account(mut)]
/// CHECK:
account: AccountInfo<'info>,
base: Signer<'info>,
}
#[derive(Accounts)]
pub struct AssignWithSeed<'info> {
#[account(mut)]
/// CHECK:
account: AccountInfo<'info>,
base: Signer<'info>,
}
#[derive(Accounts)]
pub struct TransferWithSeed<'info> {
#[account(mut)]
/// CHECK:
from: AccountInfo<'info>,
base: Signer<'info>,
#[account(mut)]
/// CHECK:
to: AccountInfo<'info>,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
pub struct FeeCalculator {
pub lamports_per_signature: u64,
}
#[account]
pub struct Nonce {
pub version: u32,
pub state: u32,
pub authorized_pubkey: Pubkey,
pub nonce: Pubkey,
pub fee_calculator: FeeCalculator,
}

View File

@ -1,22 +0,0 @@
[package]
name = "spl-associated-token"
version = "0.1.0"
description = "Created with Anchor"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
name = "spl_associated_token"
[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []
[profile.release]
overflow-checks = true
[dependencies]
anchor-lang = { path = "../../../../lang" }

View File

@ -1,2 +0,0 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -1,33 +0,0 @@
// This file is autogenerated with https://github.com/acheroncrypto/native-to-anchor
use anchor_lang::prelude::*;
declare_id!("4dUGnkre6uBhX1abB4ofkoecGN4aDXdiWSaWLUjVw6bh");
#[program]
pub mod spl_associated_token {
use super::*;
pub fn create(ctx: Context<Create>) -> Result<()> {
Ok(())
}
}
#[derive(Accounts)]
pub struct Create<'info> {
#[account(mut)]
authority: Signer<'info>,
#[account(mut)]
/// CHECK:
associated_account: AccountInfo<'info>,
/// CHECK:
owner: AccountInfo<'info>,
/// CHECK:
mint: AccountInfo<'info>,
/// CHECK:
system_program: AccountInfo<'info>,
/// CHECK:
token_program: AccountInfo<'info>,
/// CHECK:
rent: AccountInfo<'info>,
}

View File

@ -1,20 +0,0 @@
[package]
name = "spl-token"
version = "0.1.0"
description = "Created with Anchor"
rust-version = "1.56"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
name = "spl_token"
[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []
[dependencies]
anchor-lang = { path = "../../../../lang" }

Some files were not shown because too many files have changed in this diff Show More