cli, context: Add spl token faucet to init-mint command (#63)

This commit is contained in:
Armani Ferrante 2020-12-15 16:12:51 -08:00 committed by GitHub
parent 7261556aba
commit 6de9192766
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 202 additions and 27 deletions

2
Cargo.lock generated
View File

@ -3085,7 +3085,9 @@ dependencies = [
"serum-registry-cli",
"serum-registry-rewards-cli",
"serum_dex",
"solana-client",
"solana-sdk",
"spl-token 2.0.6",
]
[[package]]

View File

@ -12,13 +12,15 @@ TEST_PAYER_FILEPATH="$(HOME)/.config/solana/id.json"
#
# The solana cluster to test against. Defaults to local.
#
ifndef TEST_CLUSTER
TEST_CLUSTER=l
#TEST_CLUSTER=devnet
endif
#
# The url of TEST_CLUSTER.
#
ifndef TEST_CLUSTER_URL
TEST_CLUSTER_URL="http://localhost:8899"
#TEST_CLUSTER_URL="https://devnet.solana.com"
endif
#
# One can optionally set this along with the test-program command
# to avoid redeploying everytime tests are run.

View File

@ -9,7 +9,7 @@ name = "serum"
path = "src/main.rs"
[features]
dev = ["serum-common", "serum-common-tests", "serum_dex", "solana-sdk", "serde_json", "serum-registry"]
dev = ["serum-common", "serum-common-tests", "serum_dex", "solana-sdk", "serde_json", "serum-registry", "spl-token", "solana-client"]
default = []
@ -28,4 +28,6 @@ serum-common-tests = { path = "../common/tests", optional = true }
serum_dex = { path = "../dex", default-features = false, features = ["client"], optional = true }
solana-sdk = { version = "1.3.14", optional = true }
serde_json = { version = "1.0.59", optional = true }
serum-registry = { path = "../registry", optional = true }
serum-registry = { path = "../registry", optional = true }
spl-token = { version = "2.0.6", optional = true }
solana-client = { version = "1.4.4", optional = true }

81
cli/src/dev/faucet.rs Normal file
View File

@ -0,0 +1,81 @@
use anyhow::{anyhow, Result};
use serum_common::client::rpc;
use serum_context::Context;
use solana_client::rpc_config::RpcSendTransactionConfig;
use solana_sdk::commitment_config::CommitmentConfig;
use solana_sdk::instruction::{AccountMeta, Instruction};
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Signer;
use solana_sdk::sysvar;
use solana_sdk::transaction::Transaction;
const FAUCET_SIZE: usize = 77;
// `admin` must be the current mint authority.
//
// Faucet program here:
//
// https://github.com/paul-schaaf/spl-token-faucet/blob/main/src/program/src/instruction.rs.
pub fn create(ctx: &Context, mint: Pubkey, amount: u64, admin: Pubkey) -> Result<Pubkey> {
let faucet_pid = ctx.faucet_pid.ok_or(anyhow!("faucet not provided"))?;
let faucet = rpc::create_account_rent_exempt(
&ctx.rpc_client(),
&ctx.wallet()?,
FAUCET_SIZE,
&faucet_pid,
)?
.pubkey();
let ixs = {
let (faucet_pda, _nonce) =
Pubkey::find_program_address(&["faucet".to_string().as_bytes()], &faucet_pid);
let set_auth_ix = spl_token::instruction::set_authority(
&spl_token::ID,
&mint,
Some(&faucet_pda),
spl_token::instruction::AuthorityType::MintTokens,
&admin,
&[],
)?;
let create_faucet_ix = {
let accounts = vec![
AccountMeta::new_readonly(mint, false),
AccountMeta::new(faucet, false),
AccountMeta::new(sysvar::rent::ID, false),
AccountMeta::new_readonly(admin, false),
];
let mut data = vec![0];
data.extend_from_slice(&amount.to_le_bytes());
Instruction {
program_id: faucet_pid,
data,
accounts,
}
};
[set_auth_ix, create_faucet_ix]
};
let _tx = {
let client = ctx.rpc_client();
let payer = ctx.wallet()?;
let (recent_hash, _fee_calc) = client.get_recent_blockhash()?;
let tx =
Transaction::new_signed_with_payer(&ixs, Some(&payer.pubkey()), &[&payer], recent_hash);
let sig = client.send_and_confirm_transaction_with_spinner_and_config(
&tx,
CommitmentConfig::single(),
RpcSendTransactionConfig {
skip_preflight: true,
..RpcSendTransactionConfig::default()
},
)?;
sig
};
Ok(faucet)
}

View File

@ -13,11 +13,16 @@ use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Signer;
use std::num::NonZeroU64;
mod faucet;
#[derive(Debug, Clap)]
pub enum Command {
/// Creates 1) SRM mint, 2) MSRM mint 3) SRM funded token account, and
/// 4) MSRM funded token account, all owned by the configured wallet.
InitMint,
InitMint {
#[clap(short, long)]
faucet: bool,
},
AllocateAccount {
#[clap(short, long)]
program_id: Pubkey,
@ -34,7 +39,7 @@ pub enum Command {
pub fn run(ctx: Context, cmd: Command) -> Result<()> {
match cmd {
Command::InitMint => init_mint(&ctx),
Command::InitMint { faucet } => init_mint(&ctx, faucet),
Command::AllocateAccount { program_id, size } => allocate_account(&ctx, program_id, size),
Command::GenerateOrders {
coin_wallet,
@ -43,7 +48,7 @@ pub fn run(ctx: Context, cmd: Command) -> Result<()> {
}
}
fn init_mint(ctx: &Context) -> Result<()> {
fn init_mint(ctx: &Context, faucet: bool) -> Result<()> {
// Doesn't matter.
let program_id = Pubkey::new_from_array([0; 32]).to_string();
let payer_filepath = &ctx.wallet_path.to_string();
@ -51,13 +56,22 @@ fn init_mint(ctx: &Context) -> Result<()> {
std::env::set_var(serum_common_tests::TEST_PROGRAM_ID, program_id);
std::env::set_var(serum_common_tests::TEST_PAYER_FILEPATH, payer_filepath);
std::env::set_var(serum_common_tests::TEST_CLUSTER, cluster);
let (_client, g) = serum_common_tests::genesis::<Client>();
let (client, g) = serum_common_tests::genesis::<Client>();
let (srm_faucet, msrm_faucet) = match faucet {
false => (None, None),
true => {
let srm_faucet =
faucet::create(ctx, g.srm_mint, 1_000_000_000_000, client.payer().pubkey())?;
let msrm_faucet =
faucet::create(ctx, g.msrm_mint, 1_000_000_000_000, client.payer().pubkey())?;
(Some(srm_faucet), Some(msrm_faucet))
}
};
println!(
"{}",
serde_json::json!({
"wallet": g.wallet.to_string(),
"mintAuthority": g.mint_authority.to_string(),
"srmMint": g.srm_mint.to_string(),
"msrmMint": g.msrm_mint.to_string(),
"god": g.god.to_string(),
@ -65,6 +79,14 @@ fn init_mint(ctx: &Context) -> Result<()> {
"godBalanceBefore": g.god_balance_before,
"godMsrmBalanceBefore": g.god_msrm_balance_before,
"godOwner": g.god_owner.to_string(),
"srmFaucet": match srm_faucet {
None => "null".to_string(),
Some(f) => f.to_string(),
},
"msrmFaucet": match msrm_faucet {
None => "null".to_string(),
Some(f) => f.to_string(),
}
})
);

View File

@ -32,7 +32,7 @@ impl FromStr for Cluster {
"l" | "localnet" => Ok(Cluster::Localnet),
"g" | "debug" => Ok(Cluster::Debug),
_ => Err(anyhow::Error::msg(
"Cluster must be one of [testnet, mainnet, devnet] or be an http or https url\n",
"Cluster must be one of [localnet, testnet, mainnet, devnet] or be an http or https url\n",
)),
}
}

View File

@ -26,6 +26,7 @@ pub struct Context {
pub meta_entity_pid: Pubkey,
pub lockup_pid: Pubkey,
pub dex_pid: Pubkey,
pub faucet_pid: Option<Pubkey>,
}
impl Context {
@ -85,6 +86,7 @@ struct Programs {
pub meta_entity_pid: String,
pub lockup_pid: String,
pub dex_pid: String,
pub faucet_pid: Option<String>,
}
impl Config {
@ -98,11 +100,20 @@ impl Config {
impl TryFrom<Config> for Context {
type Error = anyhow::Error;
fn try_from(cfg: Config) -> std::result::Result<Self, anyhow::Error> {
let cluster = cfg
.network
.cluster
.map_or(Ok(Default::default()), |c| c.parse())?;
let faucet_pid = cfg
.programs
.faucet_pid
.or_else(|| match &cluster {
Cluster::Devnet => Some("4bXpkKSV8swHSnwqtzuboGPaPDeEgAn4Vt8GfarV5rZt".to_string()),
_ => None,
})
.map(|f| f.parse().unwrap());
Ok(Self {
cluster: cfg
.network
.cluster
.map_or(Ok(Default::default()), |c| c.parse())?,
cluster,
wallet_path: cfg
.wallet_path
.map_or(Default::default(), |p| WalletPath(p)),
@ -114,6 +125,7 @@ impl TryFrom<Config> for Context {
lockup_pid: cfg.programs.lockup_pid.parse()?,
meta_entity_pid: cfg.programs.meta_entity_pid.parse()?,
dex_pid: cfg.programs.dex_pid.parse()?,
faucet_pid,
})
}
}

View File

@ -6,10 +6,39 @@
# Does deployment + initialization of all programs and accounts needed to run
# the staking + lockup application.
#
# Usage:
#
# ./scripts/deploy-staking.sh <localnet | devnet | mainnet>
#
################################################################################
CLUSTER=l
#CLUSTER=devnet
set -euox pipefail
CLUSTER=$1
if [ "$CLUSTER" = "devnet" ]; then
echo "Deploying to Devnet..."
FAUCET_FLAG="--faucet"
CONFIG_FILE=~/.config/serum/cli/devnet.yaml
CLUSTER_URL="https://devnet.solana.com"
elif [ "$CLUSTER" = "mainnet" ]; then
echo "Deploying to Mainnet..."
FAUCET_FLAG=""
CONFIG_FILE=~/.config/serum/cli/mainnet.yaml
CLUSTER_URL="https://api.mainnet-beta.solana.com"
elif [ "$CLUSTER" = "localnet" ]; then
echo "Deploying to Localnet..."
FAUCET_FLAG=""
CONFIG_FILE=~/.config/serum/cli/localnet.yaml
CLUSTER_URL="http://localhost:8899"
else
echo "Invalid cluster"
exit 1
fi
#
# Seconds.
#
DEACTIVATION_TIMELOCK=60
WITHDRAWAL_TIMELOCK=60
#
@ -24,8 +53,9 @@ STAKE_RATE=1000000
# 1 MSRM (0 decimals) to stake.
#
STAKE_RATE_MEGA=1
CONFIG_FILE=~/.config/serum/cli/dev.yaml
#
# Must be built with the `dev` feature on.
#
serum=$(pwd)/target/debug/serum
main() {
@ -40,6 +70,7 @@ main() {
#
# Build all programs.
#
echo "Building all programs..."
make -s -C lockup build
make -s -C registry build
make -s -C registry/meta-entity build
@ -49,34 +80,51 @@ main() {
#
# Deploy all the programs.
#
local pids=$(make -s -C registry deploy-all)
local rewards_pids=$(make -s -C registry/rewards deploy-all)
echo "Deploying all programs..."
local pids=$(TEST_CLUSTER="$CLUSTER" TEST_CLUSTER_URL="$CLUSTER_URL" make -s -C registry deploy-all)
local rewards_pids=$(TEST_CLUSTER="$CLUSTER" TEST_CLUSTER_URL="$CLUSTER_URL" make -s -C registry/rewards deploy-all)
local registry_pid=$(echo $pids | jq .registryProgramId -r)
local lockup_pid=$(echo $pids | jq .lockupProgramId -r)
local meta_entity_pid=$(echo $pids | jq .metaEntityProgramId -r)
local dex_pid=$(echo $rewards_pids | jq .dexProgramId -r)
local rewards_pid=$(echo $rewards_pids | jq .rewardsProgramId -r)
local dex_pid=$(echo $rewards_pids | jq .dexProgramId -r)
#
# Generate genesis state.
# Generate genesis state. Use dummy accounts, if needed.
#
local genesis=$($serum dev init-mint)
local srm_mint="SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"
local msrm_mint="MSRMcoVyrFxnSgo5uXwone5SKcGhT1KEJMFEkMEWf9L"
local god="FhmUh2PEpTzUwBWPt4qgDBeqfmb2ES3T64CkT1ZiktSS" # Dummy.
local god_msrm="FhmUh2PEpTzUwBWPt4qgDBeqfmb2ES3T64CkT1ZiktSS" # Dummy.
local srm_faucet="None"
local msrm_faucet="None"
if [ "$CLUSTER" != "mainnet" ]; then
echo "Genesis initialization..."
genesis=$($serum --config $CONFIG_FILE dev init-mint $FAUCET_FLAG)
local srm_mint=$(echo $genesis | jq .srmMint -r)
local msrm_mint=$(echo $genesis | jq .msrmMint -r)
local god=$(echo $genesis | jq .god -r)
local god_msrm=$(echo $genesis | jq .godMsrm -r)
srm_mint=$(echo $genesis | jq .srmMint -r)
msrm_mint=$(echo $genesis | jq .msrmMint -r)
god=$(echo $genesis | jq .god -r)
god_msrm=$(echo $genesis | jq .godMsrm -r)
srm_faucet=$(echo $genesis | jq .srmFaucet -r)
msrm_faucet=$(echo $genesis | jq .msrmFaucet -r)
fi
#
# Write out the CLI configuration file.
#
echo "Writing config $CONFIG_FILE..."
mkdir -p $(dirname $CONFIG_FILE)
cat << EOM > $CONFIG_FILE
---
network:
cluster: $CLUSTER
#
# SRM Faucet: $srm_faucet
# MSRM Faucet: $msrm_faucet
#
mints:
srm: $srm_mint
msrm: $msrm_mint
@ -87,11 +135,13 @@ programs:
meta_entity_pid: $meta_entity_pid
lockup_pid: $lockup_pid
dex_pid: $dex_pid
EOM
#
# Now intialize all the accounts.
#
echo "Initializing registrar..."
local rInit=$($serum --config $CONFIG_FILE \
registry init \
--deactivation-timelock $DEACTIVATION_TIMELOCK \
@ -104,6 +154,7 @@ EOM
local registrar_nonce=$(echo $rInit | jq .nonce -r)
local reward_q=$(echo $rInit | jq .rewardEventQueue -r)
echo "Initializing lockup..."
local lInit=$($serum --config $CONFIG_FILE \
lockup initialize)
@ -113,6 +164,7 @@ EOM
# Initialize a node entity. Hack until we separate joining entities
# from creating member accounts.
#
echo "Creating the default node entity..."
local createEntity=$($serum --config $CONFIG_FILE \
registry create-entity \
--registrar $registrar \
@ -125,6 +177,7 @@ EOM
#
# Add the registry to the lockup program whitelist.
#
echo "Adding registry to the lockup whitelist..."
$serum --config $CONFIG_FILE \
lockup gov \
--safe $safe \
@ -136,6 +189,7 @@ EOM
#
# Log the generated TypeScript.
#
set +e
read -r -d '' VAR << EOM
{
srm: new PublicKey('${srm_mint}'),