Changes for a functioning lockup/stake UI (#46)
This commit is contained in:
parent
b9e7123f40
commit
930aa1d9f6
|
@ -263,12 +263,16 @@ fn test() -> Result<()> {
|
|||
// Switch again (todo: restore cwd in `build` command).
|
||||
set_workspace_dir_or_exit();
|
||||
|
||||
// Bootup validator.
|
||||
let mut validator_handle = start_test_validator()?;
|
||||
|
||||
// Deploy all programs.
|
||||
let cfg = Config::discover()?.expect("Inside a workspace").0;
|
||||
let programs = deploy_ws("http://localhost:8899", &cfg.wallet.to_string())?;
|
||||
|
||||
// Bootup validator.
|
||||
let validator_handle = match cfg.cluster.url() {
|
||||
"http://127.0.0.1:8899" => Some(start_test_validator()?),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let programs = deploy_ws(cfg.cluster.url(), &cfg.wallet.to_string())?;
|
||||
|
||||
// Store deployed program addresses in IDL metadata (for consumption by
|
||||
// client + tests).
|
||||
|
@ -290,14 +294,19 @@ fn test() -> Result<()> {
|
|||
.arg("-t")
|
||||
.arg("10000")
|
||||
.arg("tests/")
|
||||
.env("ANCHOR_PROVIDER_URL", cfg.cluster.url())
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit())
|
||||
.output()
|
||||
{
|
||||
if let Some(mut validator_handle) = validator_handle {
|
||||
validator_handle.kill()?;
|
||||
}
|
||||
return Err(anyhow::format_err!("{}", e.to_string()));
|
||||
}
|
||||
if let Some(mut validator_handle) = validator_handle {
|
||||
validator_handle.kill()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -350,18 +359,59 @@ fn start_test_validator() -> Result<Child> {
|
|||
Ok(validator_handle)
|
||||
}
|
||||
|
||||
// TODO: Testing and deploys should use separate sections of metadata.
|
||||
// Similarly, each network should have separate metadata.
|
||||
fn deploy(url: Option<String>, keypair: Option<String>) -> Result<()> {
|
||||
let (cfg, ws_path, _) = Config::discover()?.ok_or(anyhow!("Not in Anchor workspace."))?;
|
||||
std::env::set_current_dir(ws_path.parent().unwrap())?;
|
||||
// Build all programs.
|
||||
set_workspace_dir_or_exit();
|
||||
build(None)?;
|
||||
set_workspace_dir_or_exit();
|
||||
|
||||
// Deploy all programs.
|
||||
let cfg = Config::discover()?.expect("Inside a workspace").0;
|
||||
let url = url.unwrap_or(cfg.cluster.url().to_string());
|
||||
let keypair = keypair.unwrap_or(cfg.wallet.to_string());
|
||||
|
||||
let deployment = deploy_ws(&url, &keypair)?;
|
||||
|
||||
// Add metadata to all IDLs.
|
||||
for (program, address) in deployment {
|
||||
println!("Deployed {} at {}", program.idl.name, address.to_string());
|
||||
// Add metadata to the IDL.
|
||||
let mut idl = program.idl;
|
||||
idl.metadata = Some(serde_json::to_value(IdlTestMetadata {
|
||||
address: address.to_string(),
|
||||
})?);
|
||||
|
||||
// Persist it.
|
||||
let idl_out = PathBuf::from("target/idl")
|
||||
.join(&idl.name)
|
||||
.with_extension("json");
|
||||
write_idl(&idl, Some(&idl_out))?;
|
||||
|
||||
println!("Deployed {} at {}", idl.name, address.to_string());
|
||||
}
|
||||
|
||||
run_hosted_deploy(&url, &keypair)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_hosted_deploy(url: &str, keypair: &str) -> Result<()> {
|
||||
println!("Running deploy script");
|
||||
|
||||
let cur_dir = std::env::current_dir()?;
|
||||
let module_path = format!("{}/migrations/deploy.js", cur_dir.display());
|
||||
let deploy_script_str = template::deploy_script(url, &module_path);
|
||||
std::env::set_current_dir(".anchor")?;
|
||||
|
||||
std::fs::write("deploy.js", deploy_script_str)?;
|
||||
if let Err(e) = std::process::Command::new("node")
|
||||
.arg("deploy.js")
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit())
|
||||
.output()
|
||||
{
|
||||
std::process::exit(1);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -392,7 +442,7 @@ fn deploy_ws(url: &str, keypair: &str) -> Result<Vec<(Program, Pubkey)>> {
|
|||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct DeployStdout {
|
||||
program_id: String,
|
||||
}
|
||||
|
|
|
@ -34,6 +34,35 @@ anchor-lang = {{ git = "https://github.com/project-serum/anchor", features = ["d
|
|||
)
|
||||
}
|
||||
|
||||
pub fn deploy_script(cluster_url: &str, script_path: &str) -> String {
|
||||
format!(
|
||||
r#"
|
||||
const serumCmn = require("@project-serum/common");
|
||||
const anchor = require('@project-serum/anchor');
|
||||
|
||||
// Deploy script defined by the user.
|
||||
const userScript = require("{0}");
|
||||
|
||||
async function main() {{
|
||||
const url = "{1}";
|
||||
const preflightCommitment = 'recent';
|
||||
const connection = new anchor.web3.Connection(url, preflightCommitment);
|
||||
const wallet = serumCmn.NodeWallet.local();
|
||||
|
||||
const provider = new serumCmn.Provider(connection, wallet, {{
|
||||
preflightCommitment,
|
||||
commitment: 'recent',
|
||||
}});
|
||||
|
||||
// Run the user's deploy script.
|
||||
userScript(provider);
|
||||
}}
|
||||
main();
|
||||
"#,
|
||||
script_path, cluster_url,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn xargo_toml() -> String {
|
||||
r#"[target.bpfel-unknown-unknown.dependencies.std]
|
||||
features = []"#
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
// deploy.js is a simple deploy script to initialize a program. This is run
|
||||
// immediately after a deploy.
|
||||
|
||||
const serumCmn = require("@project-serum/common");
|
||||
const anchor = require("@project-serum/anchor");
|
||||
const PublicKey = anchor.web3.PublicKey;
|
||||
|
||||
module.exports = async function (provider) {
|
||||
// Configure client to use the provider.
|
||||
anchor.setProvider(provider);
|
||||
|
||||
// Setup genesis state.
|
||||
const registrarConfigs = await genesis(provider);
|
||||
|
||||
// Program clients.
|
||||
const lockup = anchor.workspace.Lockup;
|
||||
const registry = anchor.workspace.Registry;
|
||||
|
||||
// Registry state constructor.
|
||||
await registry.state.rpc.new({
|
||||
accounts: {
|
||||
lockupProgram: lockup.programId,
|
||||
},
|
||||
});
|
||||
|
||||
// Lockup state constructor.
|
||||
await lockup.state.rpc.new({
|
||||
accounts: {
|
||||
authority: provider.wallet.publicKey,
|
||||
},
|
||||
});
|
||||
|
||||
// Delete the default whitelist entries.
|
||||
const defaultEntry = { programId: new anchor.web3.PublicKey() };
|
||||
await lockup.state.rpc.whitelistDelete(defaultEntry, {
|
||||
accounts: {
|
||||
authority: provider.wallet.publicKey,
|
||||
},
|
||||
});
|
||||
|
||||
// Whitelist the registry.
|
||||
await lockup.state.rpc.whitelistAdd(
|
||||
{ programId: registry.programId },
|
||||
{
|
||||
accounts: {
|
||||
authority: provider.wallet.publicKey,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Initialize all registrars.
|
||||
const cfgKeys = Object.keys(registrarConfigs);
|
||||
for (let k = 0; k < cfgKeys.length; k += 1) {
|
||||
let r = registrarConfigs[cfgKeys[k]];
|
||||
const registrar = await registrarInit(
|
||||
registry,
|
||||
r.withdrawalTimelock,
|
||||
r.stakeRate,
|
||||
r.rewardQLen,
|
||||
new anchor.web3.PublicKey(r.mint)
|
||||
);
|
||||
r["registrar"] = registrar.toString();
|
||||
}
|
||||
|
||||
// Generate code for whitelisting on UIs.
|
||||
const code = generateCode(registry, lockup, registrarConfigs);
|
||||
console.log("Generated whitelisted UI addresses:", code);
|
||||
};
|
||||
|
||||
function generateCode(registry, lockup, registrarConfigs) {
|
||||
const registrars = Object.keys(registrarConfigs)
|
||||
.map((cfg) => `${cfg}: new PublicKey('${registrarConfigs[cfg].registrar}')`)
|
||||
.join(",");
|
||||
|
||||
const mints = Object.keys(registrarConfigs)
|
||||
.map((cfg) => `${cfg}: new PublicKey('${registrarConfigs[cfg].mint}')`)
|
||||
.join(",");
|
||||
|
||||
return `{
|
||||
registryProgramId: new PublicKey('${registry.programId}'),
|
||||
lockupProgramId: new PublicKey('${lockup.programId}'),
|
||||
registrars: { ${registrars} },
|
||||
mints: { ${mints} },
|
||||
}`;
|
||||
}
|
||||
|
||||
async function genesis(provider) {
|
||||
if (
|
||||
provider.connection._rpcEndpoint === "https://api.mainnet-beta.solana.com"
|
||||
) {
|
||||
return {
|
||||
srm: {
|
||||
withdrawalTimelock: 60,
|
||||
stakeRate: 1000 * 10 ** 6,
|
||||
rewardQLen: 100,
|
||||
mint: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt",
|
||||
},
|
||||
msrm: {
|
||||
withdrawalTimelock: 45,
|
||||
stakeRate: 1,
|
||||
rewardQLen: 100,
|
||||
mint: "MSRMcoVyrFxnSgo5uXwone5SKcGhT1KEJMFEkMEWf9L",
|
||||
},
|
||||
};
|
||||
} else {
|
||||
const [token1Mint, _god1] = await serumCmn.createMintAndVault(
|
||||
provider,
|
||||
new anchor.BN(10000000000000),
|
||||
undefined,
|
||||
6
|
||||
);
|
||||
const [token2Mint, _god2] = await serumCmn.createMintAndVault(
|
||||
provider,
|
||||
new anchor.BN(10000000000),
|
||||
undefined,
|
||||
0
|
||||
);
|
||||
return {
|
||||
token1: {
|
||||
withdrawalTimelock: 60,
|
||||
stakeRate: 2 * 10 ** 6,
|
||||
rewardQLen: 100,
|
||||
mint: token1Mint.toString(),
|
||||
},
|
||||
token2: {
|
||||
withdrawalTimelock: 45,
|
||||
stakeRate: 1,
|
||||
rewardQLen: 100,
|
||||
mint: token2Mint.toString(),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function registrarInit(
|
||||
registry,
|
||||
_withdrawalTimelock,
|
||||
_stakeRate,
|
||||
rewardQLen,
|
||||
mint
|
||||
) {
|
||||
const registrar = new anchor.web3.Account();
|
||||
const rewardQ = new anchor.web3.Account();
|
||||
const withdrawalTimelock = new anchor.BN(_withdrawalTimelock);
|
||||
const stakeRate = new anchor.BN(_stakeRate);
|
||||
const [
|
||||
registrarSigner,
|
||||
nonce,
|
||||
] = await anchor.web3.PublicKey.findProgramAddress(
|
||||
[registrar.publicKey.toBuffer()],
|
||||
registry.programId
|
||||
);
|
||||
const poolMint = await serumCmn.createMint(
|
||||
registry.provider,
|
||||
registrarSigner
|
||||
);
|
||||
await registry.rpc.initialize(
|
||||
mint,
|
||||
registry.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),
|
||||
],
|
||||
}
|
||||
);
|
||||
return registrar.publicKey;
|
||||
}
|
|
@ -131,8 +131,8 @@ pub mod lockup {
|
|||
}
|
||||
|
||||
// Sends funds from the lockup program to a whitelisted program.
|
||||
pub fn whitelist_withdraw(
|
||||
ctx: Context<WhitelistWithdraw>,
|
||||
pub fn whitelist_withdraw<'a, 'b, 'c, 'info>(
|
||||
ctx: Context<'a, 'b, 'c, 'info, WhitelistWithdraw<'info>>,
|
||||
instruction_data: Vec<u8>,
|
||||
amount: u64,
|
||||
) -> Result<()> {
|
||||
|
@ -157,8 +157,8 @@ pub mod lockup {
|
|||
}
|
||||
|
||||
// Sends funds from a whitelisted program back to the lockup program.
|
||||
pub fn whitelist_deposit(
|
||||
ctx: Context<WhitelistDeposit>,
|
||||
pub fn whitelist_deposit<'a, 'b, 'c, 'info>(
|
||||
ctx: Context<'a, 'b, 'c, 'info, WhitelistDeposit<'info>>,
|
||||
instruction_data: Vec<u8>,
|
||||
) -> Result<()> {
|
||||
let before_amount = ctx.accounts.transfer.vault.amount;
|
||||
|
@ -393,7 +393,7 @@ impl<'a, 'b, 'c, 'info> From<&Withdraw<'info>> for CpiContext<'a, 'b, 'c, 'info,
|
|||
|
||||
#[access_control(is_whitelisted(transfer))]
|
||||
pub fn whitelist_relay_cpi<'info>(
|
||||
transfer: &WhitelistTransfer,
|
||||
transfer: &WhitelistTransfer<'info>,
|
||||
remaining_accounts: &[AccountInfo<'info>],
|
||||
instruction_data: Vec<u8>,
|
||||
) -> Result<()> {
|
||||
|
@ -432,7 +432,9 @@ pub fn whitelist_relay_cpi<'info>(
|
|||
&[transfer.vesting.nonce],
|
||||
];
|
||||
let signer = &[&seeds[..]];
|
||||
solana_program::program::invoke_signed(&relay_instruction, &transfer.to_account_infos(), signer)
|
||||
let mut accounts = transfer.to_account_infos();
|
||||
accounts.extend_from_slice(&remaining_accounts);
|
||||
solana_program::program::invoke_signed(&relay_instruction, &accounts, signer)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
|
|
|
@ -160,6 +160,10 @@ mod registry {
|
|||
token::mint_to(cpi_ctx, spt_amount)?;
|
||||
}
|
||||
|
||||
// Update stake timestamp.
|
||||
let member = &mut ctx.accounts.member;
|
||||
member.last_stake_ts = ctx.accounts.clock.unix_timestamp;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -231,6 +235,10 @@ mod registry {
|
|||
pending_withdrawal.registrar = *ctx.accounts.registrar.to_account_info().key;
|
||||
pending_withdrawal.locked = locked;
|
||||
|
||||
// Update stake timestamp.
|
||||
let member = &mut ctx.accounts.member;
|
||||
member.last_stake_ts = ctx.accounts.clock.unix_timestamp;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -349,12 +357,14 @@ mod registry {
|
|||
let vendor = &mut ctx.accounts.vendor;
|
||||
vendor.registrar = *ctx.accounts.registrar.to_account_info().key;
|
||||
vendor.vault = *ctx.accounts.vendor_vault.to_account_info().key;
|
||||
vendor.mint = ctx.accounts.vendor_vault.mint;
|
||||
vendor.nonce = nonce;
|
||||
vendor.pool_token_supply = ctx.accounts.pool_mint.supply;
|
||||
vendor.reward_event_q_cursor = cursor;
|
||||
vendor.start_ts = ctx.accounts.clock.unix_timestamp;
|
||||
vendor.expiry_ts = expiry_ts;
|
||||
vendor.expiry_receiver = expiry_receiver;
|
||||
vendor.from = *ctx.accounts.depositor_authority.key;
|
||||
vendor.total = total;
|
||||
vendor.expired = false;
|
||||
vendor.kind = kind.clone();
|
||||
|
@ -497,6 +507,7 @@ pub struct Initialize<'info> {
|
|||
registrar: ProgramAccount<'info, Registrar>,
|
||||
#[account(init)]
|
||||
reward_event_q: ProgramAccount<'info, RewardQueue>,
|
||||
#[account("pool_mint.decimals == 0")]
|
||||
pool_mint: CpiAccount<'info, Mint>,
|
||||
rent: Sysvar<'info, Rent>,
|
||||
}
|
||||
|
@ -1096,12 +1107,14 @@ pub struct RewardEvent {
|
|||
pub struct RewardVendor {
|
||||
pub registrar: Pubkey,
|
||||
pub vault: Pubkey,
|
||||
pub mint: Pubkey,
|
||||
pub nonce: u8,
|
||||
pub pool_token_supply: u64,
|
||||
pub reward_event_q_cursor: u32,
|
||||
pub start_ts: i64,
|
||||
pub expiry_ts: i64,
|
||||
pub expiry_receiver: Pubkey,
|
||||
pub from: Pubkey,
|
||||
pub total: u64,
|
||||
pub expired: bool,
|
||||
pub kind: RewardVendorKind,
|
||||
|
|
|
@ -5,9 +5,10 @@ const TokenInstructions = require("@project-serum/serum").TokenInstructions;
|
|||
const utils = require("./utils");
|
||||
|
||||
describe("Lockup and Registry", () => {
|
||||
const provider = anchor.Provider.local();
|
||||
// Read the provider from the configured environmnet.
|
||||
const provider = anchor.Provider.env();
|
||||
|
||||
// Configure the client to use the local cluster.
|
||||
// Configure the client to use the provider.
|
||||
anchor.setProvider(provider);
|
||||
|
||||
const lockup = anchor.workspace.Lockup;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@project-serum/anchor",
|
||||
"version": "0.0.0-alpha.7",
|
||||
"version": "0.0.0-alpha.8",
|
||||
"description": "Anchor client",
|
||||
"main": "dist/cjs/index.js",
|
||||
"module": "dist/esm/index.js",
|
||||
|
@ -15,6 +15,7 @@
|
|||
"scripts": {
|
||||
"build": "yarn build:node",
|
||||
"build:node": "tsc && tsc -p tsconfig.cjs.json",
|
||||
"lint:fix": "prettier src/** -w",
|
||||
"watch": "tsc -p tsconfig.cjs.json --watch",
|
||||
"test:unit": "jest test/unit",
|
||||
"test:integration": "jest test/integration --detectOpenHandles",
|
||||
|
@ -23,14 +24,15 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@project-serum/borsh": "^0.0.1-beta.0",
|
||||
"@project-serum/common": "^0.0.1-beta.0",
|
||||
"@project-serum/lockup": "^0.0.1-beta.0",
|
||||
"@solana/spl-token": "0.0.11",
|
||||
"@solana/web3.js": "^0.90.4",
|
||||
"@types/bn.js": "^4.11.6",
|
||||
"@types/bs58": "^4.0.1",
|
||||
"bn.js": "^5.1.2",
|
||||
"bs58": "^4.0.1",
|
||||
"buffer-layout": "^1.2.0",
|
||||
"camelcase": "^5.3.1",
|
||||
"crypto-hash": "^1.3.0",
|
||||
"eventemitter3": "^4.0.7",
|
||||
"find": "^0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
141
ts/src/coder.ts
141
ts/src/coder.ts
|
@ -1,5 +1,6 @@
|
|||
import camelCase from "camelcase";
|
||||
import { Layout } from "buffer-layout";
|
||||
import { sha256 } from "crypto-hash";
|
||||
import * as borsh from "@project-serum/borsh";
|
||||
import {
|
||||
Idl,
|
||||
|
@ -11,6 +12,11 @@ import {
|
|||
} from "./idl";
|
||||
import { IdlError } from "./error";
|
||||
|
||||
/**
|
||||
* Number of bytes of the account discriminator.
|
||||
*/
|
||||
export const ACCOUNT_DISCRIMINATOR_SIZE = 8;
|
||||
|
||||
/**
|
||||
* Coder provides a facade for encoding and decoding all IDL related objects.
|
||||
*/
|
||||
|
@ -105,24 +111,30 @@ class AccountsCoder {
|
|||
this.accountLayouts = new Map();
|
||||
return;
|
||||
}
|
||||
const layouts = idl.accounts.map((acc) => {
|
||||
const layouts: [string, Layout][] = idl.accounts.map((acc) => {
|
||||
return [acc.name, IdlCoder.typeDefLayout(acc, idl.types)];
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
this.accountLayouts = new Map(layouts);
|
||||
}
|
||||
|
||||
public encode<T = any>(accountName: string, account: T): Buffer {
|
||||
public async encode<T = any>(
|
||||
accountName: string,
|
||||
account: T
|
||||
): Promise<Buffer> {
|
||||
const buffer = Buffer.alloc(1000); // TODO: use a tighter buffer.
|
||||
const layout = this.accountLayouts.get(accountName);
|
||||
const len = layout.encode(account, buffer);
|
||||
return buffer.slice(0, len);
|
||||
let accountData = buffer.slice(0, len);
|
||||
let discriminator = await accountDiscriminator(accountName);
|
||||
return Buffer.concat([discriminator, accountData]);
|
||||
}
|
||||
|
||||
public decode<T = any>(accountName: string, ix: Buffer): T {
|
||||
// Chop off the discriminator before decoding.
|
||||
const data = ix.slice(8);
|
||||
const layout = this.accountLayouts.get(accountName);
|
||||
return layout.decode(ix);
|
||||
return layout.decode(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,14 +183,20 @@ class StateCoder {
|
|||
this.layout = IdlCoder.typeDefLayout(idl.state.struct, idl.types);
|
||||
}
|
||||
|
||||
public encode<T = any>(account: T): Buffer {
|
||||
public async encode<T = any>(name: string, account: T): Promise<Buffer> {
|
||||
const buffer = Buffer.alloc(1000); // TODO: use a tighter buffer.
|
||||
const len = this.layout.encode(account, buffer);
|
||||
return buffer.slice(0, len);
|
||||
|
||||
const disc = await stateDiscriminator(name);
|
||||
const accData = buffer.slice(0, len);
|
||||
|
||||
return Buffer.concat([disc, accData]);
|
||||
}
|
||||
|
||||
public decode<T = any>(ix: Buffer): T {
|
||||
return this.layout.decode(ix);
|
||||
// Chop off discriminator.
|
||||
const data = ix.slice(8);
|
||||
return this.layout.decode(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -299,3 +317,110 @@ class IdlCoder {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculates unique 8 byte discriminator prepended to all anchor accounts.
|
||||
export async function accountDiscriminator(name: string): Promise<Buffer> {
|
||||
return Buffer.from(
|
||||
(
|
||||
await sha256(`account:${name}`, {
|
||||
outputFormat: "buffer",
|
||||
})
|
||||
).slice(0, 8)
|
||||
);
|
||||
}
|
||||
|
||||
// Calculates unique 8 byte discriminator prepended to all anchor state accounts.
|
||||
export async function stateDiscriminator(name: string): Promise<Buffer> {
|
||||
return Buffer.from(
|
||||
(
|
||||
await sha256(`state:${name}`, {
|
||||
outputFormat: "buffer",
|
||||
})
|
||||
).slice(0, 8)
|
||||
);
|
||||
}
|
||||
|
||||
// Returns the size of the type in bytes. For variable length types, just return
|
||||
// 1. Users should override this value in such cases.
|
||||
export function typeSize(idl: Idl, ty: IdlType): number {
|
||||
switch (ty) {
|
||||
case "bool":
|
||||
return 1;
|
||||
case "u8":
|
||||
return 1;
|
||||
case "i8":
|
||||
return 1;
|
||||
case "u16":
|
||||
return 2;
|
||||
case "u32":
|
||||
return 4;
|
||||
case "u64":
|
||||
return 8;
|
||||
case "i64":
|
||||
return 8;
|
||||
case "bytes":
|
||||
return 1;
|
||||
case "string":
|
||||
return 1;
|
||||
case "publicKey":
|
||||
return 32;
|
||||
default:
|
||||
// @ts-ignore
|
||||
if (ty.vec !== undefined) {
|
||||
return 1;
|
||||
}
|
||||
// @ts-ignore
|
||||
if (ty.option !== undefined) {
|
||||
// @ts-ignore
|
||||
return 1 + typeSize(ty.option);
|
||||
}
|
||||
// @ts-ignore
|
||||
if (ty.defined !== undefined) {
|
||||
// @ts-ignore
|
||||
const filtered = idl.types.filter((t) => t.name === ty.defined);
|
||||
if (filtered.length !== 1) {
|
||||
throw new IdlError(`Type not found: ${JSON.stringify(ty)}`);
|
||||
}
|
||||
let typeDef = filtered[0];
|
||||
|
||||
return accountSize(idl, typeDef);
|
||||
}
|
||||
throw new Error(`Invalid type ${JSON.stringify(ty)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function accountSize(
|
||||
idl: Idl,
|
||||
idlAccount: IdlTypeDef
|
||||
): number | undefined {
|
||||
if (idlAccount.type.kind === "enum") {
|
||||
let variantSizes = idlAccount.type.variants.map(
|
||||
(variant: IdlEnumVariant) => {
|
||||
if (variant.fields === undefined) {
|
||||
return 0;
|
||||
}
|
||||
// @ts-ignore
|
||||
return (
|
||||
variant.fields
|
||||
// @ts-ignore
|
||||
.map((f: IdlField | IdlType) => {
|
||||
// @ts-ignore
|
||||
if (f.name === undefined) {
|
||||
throw new Error("Tuple enum variants not yet implemented.");
|
||||
}
|
||||
// @ts-ignore
|
||||
return typeSize(idl, f.type);
|
||||
})
|
||||
.reduce((a: number, b: number) => a + b)
|
||||
);
|
||||
}
|
||||
);
|
||||
return Math.max(...variantSizes) + 1;
|
||||
}
|
||||
if (idlAccount.type.fields === undefined) {
|
||||
return 0;
|
||||
}
|
||||
return idlAccount.type.fields
|
||||
.map((f) => typeSize(idl, f.type))
|
||||
.reduce((a, b) => a + b);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import BN from "bn.js";
|
||||
import * as web3 from "@solana/web3.js";
|
||||
import { Provider } from "@project-serum/common";
|
||||
import Provider, { NodeWallet as Wallet } from "./provider";
|
||||
import { Program } from "./program";
|
||||
import Coder from "./coder";
|
||||
import { Idl } from "./idl";
|
||||
import workspace from "./workspace";
|
||||
import utils from "./utils";
|
||||
import { ProgramAccount } from "./rpc";
|
||||
|
||||
let _provider: Provider | null = null;
|
||||
|
||||
|
@ -12,16 +15,23 @@ function setProvider(provider: Provider) {
|
|||
}
|
||||
|
||||
function getProvider(): Provider {
|
||||
if (_provider === null) {
|
||||
return Provider.local();
|
||||
}
|
||||
return _provider;
|
||||
}
|
||||
|
||||
export {
|
||||
workspace,
|
||||
Program,
|
||||
ProgramAccount,
|
||||
Coder,
|
||||
setProvider,
|
||||
getProvider,
|
||||
Provider,
|
||||
BN,
|
||||
web3,
|
||||
Idl,
|
||||
utils,
|
||||
Wallet,
|
||||
};
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { PublicKey } from "@solana/web3.js";
|
||||
import Provider from "./provider";
|
||||
import { RpcFactory } from "./rpc";
|
||||
import { Idl } from "./idl";
|
||||
import Coder from "./coder";
|
||||
import { Rpcs, Ixs, Txs, Accounts, State } from "./rpc";
|
||||
import { getProvider } from "./";
|
||||
|
||||
/**
|
||||
* Program is the IDL deserialized representation of a Solana program.
|
||||
|
@ -49,9 +51,15 @@ export class Program {
|
|||
*/
|
||||
readonly state: State;
|
||||
|
||||
public constructor(idl: Idl, programId: PublicKey) {
|
||||
/**
|
||||
* Wallet and network provider.
|
||||
*/
|
||||
readonly provider: Provider;
|
||||
|
||||
public constructor(idl: Idl, programId: PublicKey, provider?: Provider) {
|
||||
this.idl = idl;
|
||||
this.programId = programId;
|
||||
this.provider = provider ?? getProvider();
|
||||
|
||||
// Build the serializer.
|
||||
const coder = new Coder(idl);
|
||||
|
@ -60,7 +68,8 @@ export class Program {
|
|||
const [rpcs, ixs, txs, accounts, state] = RpcFactory.build(
|
||||
idl,
|
||||
coder,
|
||||
programId
|
||||
programId,
|
||||
this.provider
|
||||
);
|
||||
this.rpc = rpcs;
|
||||
this.instruction = ixs;
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
import {
|
||||
Connection,
|
||||
Account,
|
||||
PublicKey,
|
||||
Transaction,
|
||||
TransactionSignature,
|
||||
ConfirmOptions,
|
||||
sendAndConfirmRawTransaction,
|
||||
} from "@solana/web3.js";
|
||||
|
||||
export default class Provider {
|
||||
constructor(
|
||||
readonly connection: Connection,
|
||||
readonly wallet: Wallet,
|
||||
readonly opts: ConfirmOptions
|
||||
) {}
|
||||
|
||||
static defaultOptions(): ConfirmOptions {
|
||||
return {
|
||||
preflightCommitment: "recent",
|
||||
commitment: "recent",
|
||||
};
|
||||
}
|
||||
|
||||
// Node only api.
|
||||
static local(url?: string, opts?: ConfirmOptions): Provider {
|
||||
opts = opts || Provider.defaultOptions();
|
||||
const connection = new Connection(
|
||||
url || "http://localhost:8899",
|
||||
opts.preflightCommitment
|
||||
);
|
||||
const wallet = NodeWallet.local();
|
||||
return new Provider(connection, wallet, opts);
|
||||
}
|
||||
|
||||
// Node only api.
|
||||
static env(): Provider {
|
||||
const process = require("process");
|
||||
const url = process.env.ANCHOR_PROVIDER_URL;
|
||||
if (url === undefined) {
|
||||
throw new Error("ANCHOR_PROVIDER_URL is not defined");
|
||||
}
|
||||
const options = Provider.defaultOptions();
|
||||
const connection = new Connection(url, options.commitment);
|
||||
const wallet = NodeWallet.local();
|
||||
|
||||
return new Provider(connection, wallet, options);
|
||||
}
|
||||
|
||||
async send(
|
||||
tx: Transaction,
|
||||
signers?: Array<Account | undefined>,
|
||||
opts?: ConfirmOptions
|
||||
): Promise<TransactionSignature> {
|
||||
if (signers === undefined) {
|
||||
signers = [];
|
||||
}
|
||||
if (opts === undefined) {
|
||||
opts = this.opts;
|
||||
}
|
||||
|
||||
const signerKps = signers.filter((s) => s !== undefined) as Array<Account>;
|
||||
const signerPubkeys = [this.wallet.publicKey].concat(
|
||||
signerKps.map((s) => s.publicKey)
|
||||
);
|
||||
|
||||
tx.setSigners(...signerPubkeys);
|
||||
tx.recentBlockhash = (
|
||||
await this.connection.getRecentBlockhash(opts.preflightCommitment)
|
||||
).blockhash;
|
||||
|
||||
await this.wallet.signTransaction(tx);
|
||||
signerKps.forEach((kp) => {
|
||||
tx.partialSign(kp);
|
||||
});
|
||||
|
||||
const rawTx = tx.serialize();
|
||||
|
||||
const txId = await sendAndConfirmRawTransaction(
|
||||
this.connection,
|
||||
rawTx,
|
||||
opts
|
||||
);
|
||||
|
||||
return txId;
|
||||
}
|
||||
|
||||
async sendAll(
|
||||
reqs: Array<SendTxRequest>,
|
||||
opts?: ConfirmOptions
|
||||
): Promise<Array<TransactionSignature>> {
|
||||
if (opts === undefined) {
|
||||
opts = this.opts;
|
||||
}
|
||||
const blockhash = await this.connection.getRecentBlockhash(
|
||||
opts.preflightCommitment
|
||||
);
|
||||
|
||||
let txs = reqs.map((r) => {
|
||||
let tx = r.tx;
|
||||
let signers = r.signers;
|
||||
|
||||
if (signers === undefined) {
|
||||
signers = [];
|
||||
}
|
||||
|
||||
const signerKps = signers.filter(
|
||||
(s) => s !== undefined
|
||||
) as Array<Account>;
|
||||
const signerPubkeys = [this.wallet.publicKey].concat(
|
||||
signerKps.map((s) => s.publicKey)
|
||||
);
|
||||
|
||||
tx.setSigners(...signerPubkeys);
|
||||
tx.recentBlockhash = blockhash.blockhash;
|
||||
signerKps.forEach((kp) => {
|
||||
tx.partialSign(kp);
|
||||
});
|
||||
|
||||
return tx;
|
||||
});
|
||||
|
||||
const signedTxs = await this.wallet.signAllTransactions(txs);
|
||||
|
||||
const sigs = [];
|
||||
|
||||
for (let k = 0; k < txs.length; k += 1) {
|
||||
const tx = signedTxs[k];
|
||||
const rawTx = tx.serialize();
|
||||
sigs.push(
|
||||
await sendAndConfirmRawTransaction(this.connection, rawTx, opts)
|
||||
);
|
||||
}
|
||||
|
||||
return sigs;
|
||||
}
|
||||
}
|
||||
|
||||
export type SendTxRequest = {
|
||||
tx: Transaction;
|
||||
signers: Array<Account | undefined>;
|
||||
};
|
||||
|
||||
export interface Wallet {
|
||||
signTransaction(tx: Transaction): Promise<Transaction>;
|
||||
signAllTransactions(txs: Transaction[]): Promise<Transaction[]>;
|
||||
publicKey: PublicKey;
|
||||
}
|
||||
|
||||
export class NodeWallet implements Wallet {
|
||||
constructor(readonly payer: Account) {}
|
||||
|
||||
static local(): NodeWallet {
|
||||
const payer = new Account(
|
||||
Buffer.from(
|
||||
JSON.parse(
|
||||
require("fs").readFileSync(
|
||||
require("os").homedir() + "/.config/solana/id.json",
|
||||
{
|
||||
encoding: "utf-8",
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
return new NodeWallet(payer);
|
||||
}
|
||||
|
||||
async signTransaction(tx: Transaction): Promise<Transaction> {
|
||||
tx.partialSign(this.payer);
|
||||
return tx;
|
||||
}
|
||||
|
||||
async signAllTransactions(txs: Transaction[]): Promise<Transaction[]> {
|
||||
return txs.map((t) => {
|
||||
t.partialSign(this.payer);
|
||||
return t;
|
||||
});
|
||||
}
|
||||
|
||||
get publicKey(): PublicKey {
|
||||
return this.payer.publicKey;
|
||||
}
|
||||
}
|
553
ts/src/rpc.ts
553
ts/src/rpc.ts
|
@ -1,4 +1,6 @@
|
|||
import camelCase from "camelcase";
|
||||
import EventEmitter from "eventemitter3";
|
||||
import * as bs58 from "bs58";
|
||||
import {
|
||||
Account,
|
||||
AccountMeta,
|
||||
|
@ -9,42 +11,41 @@ import {
|
|||
TransactionSignature,
|
||||
TransactionInstruction,
|
||||
SYSVAR_RENT_PUBKEY,
|
||||
Commitment,
|
||||
} from "@solana/web3.js";
|
||||
import { sha256 } from "crypto-hash";
|
||||
import Provider from "./provider";
|
||||
import {
|
||||
Idl,
|
||||
IdlAccount,
|
||||
IdlInstruction,
|
||||
IdlTypeDef,
|
||||
IdlType,
|
||||
IdlField,
|
||||
IdlEnumVariant,
|
||||
IdlAccountItem,
|
||||
IdlStateMethod,
|
||||
} from "./idl";
|
||||
import { IdlError, ProgramError } from "./error";
|
||||
import Coder from "./coder";
|
||||
import { getProvider } from "./";
|
||||
import Coder, {
|
||||
ACCOUNT_DISCRIMINATOR_SIZE,
|
||||
accountDiscriminator,
|
||||
stateDiscriminator,
|
||||
accountSize,
|
||||
} from "./coder";
|
||||
|
||||
/**
|
||||
* Number of bytes of the account discriminator.
|
||||
*/
|
||||
const ACCOUNT_DISCRIMINATOR_SIZE = 8;
|
||||
|
||||
/**
|
||||
* Rpcs is a dynamically generated object with rpc methods attached.
|
||||
* Dynamically generated rpc namespace.
|
||||
*/
|
||||
export interface Rpcs {
|
||||
[key: string]: RpcFn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ixs is a dynamically generated object with ix functions attached.
|
||||
* Dynamically generated instruction namespace.
|
||||
*/
|
||||
export interface Ixs {
|
||||
[key: string]: IxFn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically generated transaction namespace.
|
||||
*/
|
||||
export interface Txs {
|
||||
[key: string]: TxFn;
|
||||
}
|
||||
|
@ -65,7 +66,11 @@ export type RpcFn = (...args: any[]) => Promise<TransactionSignature>;
|
|||
/**
|
||||
* Ix is a function to create a `TransactionInstruction` generated from an IDL.
|
||||
*/
|
||||
export type IxFn = (...args: any[]) => TransactionInstruction;
|
||||
export type IxFn = IxProps & ((...args: any[]) => TransactionInstruction);
|
||||
|
||||
type IxProps = {
|
||||
accounts: (ctx: RpcAccounts) => any;
|
||||
};
|
||||
|
||||
/**
|
||||
* Tx is a function to create a `Transaction` generate from an IDL.
|
||||
|
@ -75,7 +80,26 @@ export type TxFn = (...args: any[]) => Transaction;
|
|||
/**
|
||||
* Account is a function returning a deserialized account, given an address.
|
||||
*/
|
||||
export type AccountFn<T = any> = (address: PublicKey) => T;
|
||||
export type AccountFn<T = any> = AccountProps & ((address: PublicKey) => T);
|
||||
|
||||
/**
|
||||
* Deserialized account owned by a program.
|
||||
*/
|
||||
export type ProgramAccount<T = any> = {
|
||||
publicKey: PublicKey;
|
||||
account: T;
|
||||
};
|
||||
|
||||
/**
|
||||
* Non function properties on the acccount namespace.
|
||||
*/
|
||||
type AccountProps = {
|
||||
size: number;
|
||||
all: (filter?: Buffer) => Promise<ProgramAccount<any>[]>;
|
||||
subscribe: (address: PublicKey, commitment?: Commitment) => EventEmitter;
|
||||
unsubscribe: (address: PublicKey) => void;
|
||||
createInstruction: (account: Account) => Promise<TransactionInstruction>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Options for an RPC invocation.
|
||||
|
@ -112,6 +136,9 @@ export type State = {
|
|||
rpc: Rpcs;
|
||||
};
|
||||
|
||||
// Tracks all subscriptions.
|
||||
const subscriptions: Map<string, Subscription> = new Map();
|
||||
|
||||
/**
|
||||
* RpcFactory builds an Rpcs object for a given IDL.
|
||||
*/
|
||||
|
@ -124,177 +151,150 @@ export class RpcFactory {
|
|||
public static build(
|
||||
idl: Idl,
|
||||
coder: Coder,
|
||||
programId: PublicKey
|
||||
programId: PublicKey,
|
||||
provider: Provider
|
||||
): [Rpcs, Ixs, Txs, Accounts, State] {
|
||||
const idlErrors = parseIdlErrors(idl);
|
||||
|
||||
const rpcs: Rpcs = {};
|
||||
const ixFns: Ixs = {};
|
||||
const txFns: Txs = {};
|
||||
const state = RpcFactory.buildState(idl, coder, programId, idlErrors);
|
||||
const state = RpcFactory.buildState(
|
||||
idl,
|
||||
coder,
|
||||
programId,
|
||||
idlErrors,
|
||||
provider
|
||||
);
|
||||
|
||||
idl.instructions.forEach((idlIx) => {
|
||||
const name = camelCase(idlIx.name);
|
||||
// Function to create a raw `TransactionInstruction`.
|
||||
const ix = RpcFactory.buildIx(idlIx, coder, programId);
|
||||
// Ffnction to create a `Transaction`.
|
||||
const tx = RpcFactory.buildTx(idlIx, ix);
|
||||
// Function to invoke an RPC against a cluster.
|
||||
const rpc = RpcFactory.buildRpc(idlIx, tx, idlErrors);
|
||||
|
||||
const name = camelCase(idlIx.name);
|
||||
const rpc = RpcFactory.buildRpc(idlIx, tx, idlErrors, provider);
|
||||
rpcs[name] = rpc;
|
||||
ixFns[name] = ix;
|
||||
txFns[name] = tx;
|
||||
});
|
||||
|
||||
const accountFns = idl.accounts
|
||||
? RpcFactory.buildAccounts(idl, coder, programId)
|
||||
? RpcFactory.buildAccounts(idl, coder, programId, provider)
|
||||
: {};
|
||||
|
||||
return [rpcs, ixFns, txFns, accountFns, state];
|
||||
}
|
||||
|
||||
// Builds the state namespace.
|
||||
private static buildState(
|
||||
idl: Idl,
|
||||
coder: Coder,
|
||||
programId: PublicKey,
|
||||
idlErrors: Map<number, string>
|
||||
idlErrors: Map<number, string>,
|
||||
provider: Provider
|
||||
): State | undefined {
|
||||
if (idl.state === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
let address = async () => {
|
||||
let [registrySigner, _nonce] = await PublicKey.findProgramAddress(
|
||||
[],
|
||||
programId
|
||||
);
|
||||
return PublicKey.createWithSeed(registrySigner, "unversioned", programId);
|
||||
};
|
||||
|
||||
const rpc: Rpcs = {};
|
||||
idl.state.methods.forEach((m: IdlStateMethod) => {
|
||||
if (m.name === "new") {
|
||||
// Ctor `new` method.
|
||||
rpc[m.name] = async (...args: any[]): Promise<TransactionSignature> => {
|
||||
const [ixArgs, ctx] = splitArgsAndCtx(m, [...args]);
|
||||
const tx = new Transaction();
|
||||
const [programSigner, _nonce] = await PublicKey.findProgramAddress(
|
||||
[],
|
||||
programId
|
||||
);
|
||||
const ix = new TransactionInstruction({
|
||||
keys: [
|
||||
{
|
||||
pubkey: getProvider().wallet.publicKey,
|
||||
isWritable: false,
|
||||
isSigner: true,
|
||||
},
|
||||
{ pubkey: await address(), isWritable: true, isSigner: false },
|
||||
{ pubkey: programSigner, isWritable: false, isSigner: false },
|
||||
{
|
||||
pubkey: SystemProgram.programId,
|
||||
isWritable: false,
|
||||
isSigner: false,
|
||||
},
|
||||
|
||||
{ pubkey: programId, isWritable: false, isSigner: false },
|
||||
{
|
||||
pubkey: SYSVAR_RENT_PUBKEY,
|
||||
isWritable: false,
|
||||
isSigner: false,
|
||||
},
|
||||
].concat(RpcFactory.accountsArray(ctx.accounts, m.accounts)),
|
||||
programId,
|
||||
data: coder.instruction.encode(toInstruction(m, ...ixArgs)),
|
||||
});
|
||||
|
||||
tx.add(ix);
|
||||
|
||||
const provider = getProvider();
|
||||
if (provider === null) {
|
||||
throw new Error("Provider not found");
|
||||
}
|
||||
try {
|
||||
const txSig = await provider.send(tx, ctx.signers, ctx.options);
|
||||
return txSig;
|
||||
} catch (err) {
|
||||
let translatedErr = translateError(idlErrors, err);
|
||||
if (translatedErr === null) {
|
||||
throw err;
|
||||
}
|
||||
throw translatedErr;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
rpc[m.name] = async (...args: any[]): Promise<TransactionSignature> => {
|
||||
const [ixArgs, ctx] = splitArgsAndCtx(m, [...args]);
|
||||
validateAccounts(m.accounts, ctx.accounts);
|
||||
const tx = new Transaction();
|
||||
|
||||
const keys = [
|
||||
{ pubkey: await address(), isWritable: true, isSigner: false },
|
||||
].concat(RpcFactory.accountsArray(ctx.accounts, m.accounts));
|
||||
|
||||
tx.add(
|
||||
new TransactionInstruction({
|
||||
keys,
|
||||
programId,
|
||||
data: coder.instruction.encode(toInstruction(m, ...ixArgs)),
|
||||
})
|
||||
);
|
||||
|
||||
const provider = getProvider();
|
||||
if (provider === null) {
|
||||
throw new Error("Provider not found");
|
||||
}
|
||||
try {
|
||||
const txSig = await provider.send(tx, ctx.signers, ctx.options);
|
||||
return txSig;
|
||||
} catch (err) {
|
||||
let translatedErr = translateError(idlErrors, err);
|
||||
if (translatedErr === null) {
|
||||
throw err;
|
||||
}
|
||||
throw translatedErr;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// Fetches the state object from the blockchain.
|
||||
const state = async (): Promise<any> => {
|
||||
const addr = await address();
|
||||
const provider = getProvider();
|
||||
if (provider === null) {
|
||||
throw new Error("Provider not set");
|
||||
}
|
||||
const addr = await programStateAddress(programId);
|
||||
const accountInfo = await provider.connection.getAccountInfo(addr);
|
||||
if (accountInfo === null) {
|
||||
throw new Error(`Entity does not exist ${address}`);
|
||||
throw new Error(`Account does not exist ${addr.toString()}`);
|
||||
}
|
||||
// Assert the account discriminator is correct.
|
||||
const expectedDiscriminator = Buffer.from(
|
||||
(
|
||||
await sha256(`state:${idl.state.struct.name}`, {
|
||||
outputFormat: "buffer",
|
||||
})
|
||||
).slice(0, 8)
|
||||
const expectedDiscriminator = await stateDiscriminator(
|
||||
idl.state.struct.name
|
||||
);
|
||||
const discriminator = accountInfo.data.slice(0, 8);
|
||||
if (expectedDiscriminator.compare(discriminator)) {
|
||||
if (expectedDiscriminator.compare(accountInfo.data.slice(0, 8))) {
|
||||
throw new Error("Invalid account discriminator");
|
||||
}
|
||||
// Chop off the discriminator before decoding.
|
||||
const data = accountInfo.data.slice(8);
|
||||
return coder.state.decode(data);
|
||||
return coder.state.decode(accountInfo.data);
|
||||
};
|
||||
|
||||
state["address"] = address;
|
||||
// Namespace with all rpc functions.
|
||||
const rpc: Rpcs = {};
|
||||
idl.state.methods.forEach((m: IdlStateMethod) => {
|
||||
rpc[m.name] = async (...args: any[]): Promise<TransactionSignature> => {
|
||||
const [ixArgs, ctx] = splitArgsAndCtx(m, [...args]);
|
||||
const keys = await stateInstructionKeys(programId, provider, m, ctx);
|
||||
const tx = new Transaction();
|
||||
tx.add(
|
||||
new TransactionInstruction({
|
||||
keys: keys.concat(
|
||||
RpcFactory.accountsArray(ctx.accounts, m.accounts)
|
||||
),
|
||||
programId,
|
||||
data: coder.instruction.encode(toInstruction(m, ...ixArgs)),
|
||||
})
|
||||
);
|
||||
try {
|
||||
const txSig = await provider.send(tx, ctx.signers, ctx.options);
|
||||
return txSig;
|
||||
} catch (err) {
|
||||
let translatedErr = translateError(idlErrors, err);
|
||||
if (translatedErr === null) {
|
||||
throw err;
|
||||
}
|
||||
throw translatedErr;
|
||||
}
|
||||
};
|
||||
});
|
||||
state["rpc"] = rpc;
|
||||
|
||||
// Calculates the address of the program's global state object account.
|
||||
state["address"] = async (): Promise<PublicKey> =>
|
||||
programStateAddress(programId);
|
||||
|
||||
// Subscription singleton.
|
||||
let sub: null | Subscription = null;
|
||||
|
||||
// Subscribe to account changes.
|
||||
state["subscribe"] = (commitment?: Commitment): EventEmitter => {
|
||||
if (sub !== null) {
|
||||
return sub.ee;
|
||||
}
|
||||
const ee = new EventEmitter();
|
||||
|
||||
state["address"]().then((address) => {
|
||||
const listener = provider.connection.onAccountChange(
|
||||
address,
|
||||
(acc) => {
|
||||
const account = coder.state.decode(acc.data);
|
||||
ee.emit("change", account);
|
||||
},
|
||||
commitment
|
||||
);
|
||||
|
||||
sub = {
|
||||
ee,
|
||||
listener,
|
||||
};
|
||||
});
|
||||
|
||||
return ee;
|
||||
};
|
||||
|
||||
// Unsubscribe from account changes.
|
||||
state["unsubscribe"] = () => {
|
||||
if (sub !== null) {
|
||||
provider.connection
|
||||
.removeAccountChangeListener(sub.listener)
|
||||
.then(async () => {
|
||||
sub = null;
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
};
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
// Builds the instuction namespace.
|
||||
private static buildIx(
|
||||
idlIx: IdlInstruction,
|
||||
coder: Coder,
|
||||
|
@ -357,22 +357,21 @@ export class RpcFactory {
|
|||
.flat();
|
||||
}
|
||||
|
||||
// Builds the rpc namespace.
|
||||
private static buildRpc(
|
||||
idlIx: IdlInstruction,
|
||||
txFn: TxFn,
|
||||
idlErrors: Map<number, string>
|
||||
idlErrors: Map<number, string>,
|
||||
provider: Provider
|
||||
): RpcFn {
|
||||
const rpc = async (...args: any[]): Promise<TransactionSignature> => {
|
||||
const tx = txFn(...args);
|
||||
const [_, ctx] = splitArgsAndCtx(idlIx, [...args]);
|
||||
const provider = getProvider();
|
||||
if (provider === null) {
|
||||
throw new Error("Provider not found");
|
||||
}
|
||||
try {
|
||||
const txSig = await provider.send(tx, ctx.signers, ctx.options);
|
||||
return txSig;
|
||||
} catch (err) {
|
||||
console.log("Translating error", err);
|
||||
let translatedErr = translateError(idlErrors, err);
|
||||
if (translatedErr === null) {
|
||||
throw err;
|
||||
|
@ -384,6 +383,7 @@ export class RpcFactory {
|
|||
return rpc;
|
||||
}
|
||||
|
||||
// Builds the transaction namespace.
|
||||
private static buildTx(idlIx: IdlInstruction, ixFn: IxFn): TxFn {
|
||||
const txFn = (...args: any[]): Transaction => {
|
||||
const [_, ctx] = splitArgsAndCtx(idlIx, [...args]);
|
||||
|
@ -398,52 +398,48 @@ export class RpcFactory {
|
|||
return txFn;
|
||||
}
|
||||
|
||||
// Returns the generated accounts namespace.
|
||||
private static buildAccounts(
|
||||
idl: Idl,
|
||||
coder: Coder,
|
||||
programId: PublicKey
|
||||
programId: PublicKey,
|
||||
provider: Provider
|
||||
): Accounts {
|
||||
const accountFns: Accounts = {};
|
||||
|
||||
idl.accounts.forEach((idlAccount) => {
|
||||
const accountFn = async (address: PublicKey): Promise<any> => {
|
||||
const provider = getProvider();
|
||||
if (provider === null) {
|
||||
throw new Error("Provider not set");
|
||||
}
|
||||
const name = camelCase(idlAccount.name);
|
||||
|
||||
// Fetches the decoded account from the network.
|
||||
const accountsNamespace = async (address: PublicKey): Promise<any> => {
|
||||
const accountInfo = await provider.connection.getAccountInfo(address);
|
||||
if (accountInfo === null) {
|
||||
throw new Error(`Entity does not exist ${address}`);
|
||||
throw new Error(`Account does not exist ${address.toString()}`);
|
||||
}
|
||||
|
||||
// Assert the account discriminator is correct.
|
||||
const expectedDiscriminator = Buffer.from(
|
||||
(
|
||||
await sha256(`account:${idlAccount.name}`, {
|
||||
outputFormat: "buffer",
|
||||
})
|
||||
).slice(0, 8)
|
||||
);
|
||||
const discriminator = accountInfo.data.slice(0, 8);
|
||||
|
||||
if (expectedDiscriminator.compare(discriminator)) {
|
||||
const discriminator = await accountDiscriminator(idlAccount.name);
|
||||
if (discriminator.compare(accountInfo.data.slice(0, 8))) {
|
||||
throw new Error("Invalid account discriminator");
|
||||
}
|
||||
|
||||
// Chop off the discriminator before decoding.
|
||||
const data = accountInfo.data.slice(8);
|
||||
return coder.accounts.decode(idlAccount.name, data);
|
||||
return coder.accounts.decode(idlAccount.name, accountInfo.data);
|
||||
};
|
||||
const name = camelCase(idlAccount.name);
|
||||
accountFns[name] = accountFn;
|
||||
const size = ACCOUNT_DISCRIMINATOR_SIZE + accountSize(idl, idlAccount);
|
||||
|
||||
// Returns the size of the account.
|
||||
// @ts-ignore
|
||||
accountFns[name]["size"] = size;
|
||||
accountsNamespace["size"] =
|
||||
ACCOUNT_DISCRIMINATOR_SIZE + accountSize(idl, idlAccount);
|
||||
|
||||
// Returns an instruction for creating this account.
|
||||
// @ts-ignore
|
||||
accountFns[name]["createInstruction"] = async (
|
||||
accountsNamespace["createInstruction"] = async (
|
||||
account: Account,
|
||||
sizeOverride?: number
|
||||
): Promise<TransactionInstruction> => {
|
||||
const provider = getProvider();
|
||||
// @ts-ignore
|
||||
const size = accountsNamespace["size"];
|
||||
|
||||
return SystemProgram.createAccount({
|
||||
fromPubkey: provider.wallet.publicKey,
|
||||
newAccountPubkey: account.publicKey,
|
||||
|
@ -454,11 +450,102 @@ export class RpcFactory {
|
|||
programId,
|
||||
});
|
||||
};
|
||||
|
||||
// Subscribes to all changes to this account.
|
||||
// @ts-ignore
|
||||
accountsNamespace["subscribe"] = (
|
||||
address: PublicKey,
|
||||
commitment?: Commitment
|
||||
): EventEmitter => {
|
||||
if (subscriptions.get(address.toString())) {
|
||||
return subscriptions.get(address.toString()).ee;
|
||||
}
|
||||
const ee = new EventEmitter();
|
||||
|
||||
const listener = provider.connection.onAccountChange(
|
||||
address,
|
||||
(acc) => {
|
||||
const account = coder.accounts.decode(idlAccount.name, acc.data);
|
||||
ee.emit("change", account);
|
||||
},
|
||||
commitment
|
||||
);
|
||||
|
||||
subscriptions.set(address.toString(), {
|
||||
ee,
|
||||
listener,
|
||||
});
|
||||
|
||||
return ee;
|
||||
};
|
||||
|
||||
// Unsubscribes to account changes.
|
||||
// @ts-ignore
|
||||
accountsNamespace["unsubscribe"] = (address: PublicKey) => {
|
||||
let sub = subscriptions.get(address.toString());
|
||||
if (subscriptions) {
|
||||
provider.connection
|
||||
.removeAccountChangeListener(sub.listener)
|
||||
.then(() => {
|
||||
subscriptions.delete(address.toString());
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
};
|
||||
|
||||
// Returns all instances of this account type for the program.
|
||||
// @ts-ignore
|
||||
accountsNamespace["all"] = async (
|
||||
filter?: Buffer
|
||||
): Promise<ProgramAccount<any>[]> => {
|
||||
let bytes = await accountDiscriminator(idlAccount.name);
|
||||
if (filter !== undefined) {
|
||||
bytes = Buffer.concat([bytes, filter]);
|
||||
}
|
||||
// @ts-ignore
|
||||
let resp = await provider.connection._rpcRequest("getProgramAccounts", [
|
||||
programId.toBase58(),
|
||||
{
|
||||
commitment: provider.connection.commitment,
|
||||
filters: [
|
||||
{
|
||||
memcmp: {
|
||||
offset: 0,
|
||||
bytes: bs58.encode(bytes),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
if (resp.error) {
|
||||
console.error(resp);
|
||||
throw new Error("Failed to get accounts");
|
||||
}
|
||||
return (
|
||||
resp.result
|
||||
// @ts-ignore
|
||||
.map(({ pubkey, account: { data } }) => {
|
||||
data = bs58.decode(data);
|
||||
return {
|
||||
publicKey: new PublicKey(pubkey),
|
||||
account: coder.accounts.decode(idlAccount.name, data),
|
||||
};
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
accountFns[name] = accountsNamespace;
|
||||
});
|
||||
|
||||
return accountFns;
|
||||
}
|
||||
}
|
||||
|
||||
type Subscription = {
|
||||
listener: number;
|
||||
ee: EventEmitter;
|
||||
};
|
||||
|
||||
function translateError(
|
||||
idlErrors: Map<number, string>,
|
||||
err: any
|
||||
|
@ -550,84 +637,62 @@ function validateInstruction(ix: IdlInstruction, ...args: any[]) {
|
|||
// todo
|
||||
}
|
||||
|
||||
function accountSize(idl: Idl, idlAccount: IdlTypeDef): number | undefined {
|
||||
if (idlAccount.type.kind === "enum") {
|
||||
let variantSizes = idlAccount.type.variants.map(
|
||||
(variant: IdlEnumVariant) => {
|
||||
if (variant.fields === undefined) {
|
||||
return 0;
|
||||
}
|
||||
// @ts-ignore
|
||||
return (
|
||||
variant.fields
|
||||
// @ts-ignore
|
||||
.map((f: IdlField | IdlType) => {
|
||||
// @ts-ignore
|
||||
if (f.name === undefined) {
|
||||
throw new Error("Tuple enum variants not yet implemented.");
|
||||
}
|
||||
// @ts-ignore
|
||||
return typeSize(idl, f.type);
|
||||
})
|
||||
.reduce((a: number, b: number) => a + b)
|
||||
// Calculates the deterministic address of the program's "state" account.
|
||||
async function programStateAddress(programId: PublicKey): Promise<PublicKey> {
|
||||
let [registrySigner, _nonce] = await PublicKey.findProgramAddress(
|
||||
[],
|
||||
programId
|
||||
);
|
||||
}
|
||||
);
|
||||
return Math.max(...variantSizes) + 1;
|
||||
}
|
||||
if (idlAccount.type.fields === undefined) {
|
||||
return 0;
|
||||
}
|
||||
return idlAccount.type.fields
|
||||
.map((f) => typeSize(idl, f.type))
|
||||
.reduce((a, b) => a + b);
|
||||
return PublicKey.createWithSeed(registrySigner, "unversioned", programId);
|
||||
}
|
||||
|
||||
// Returns the size of the type in bytes. For variable length types, just return
|
||||
// 1. Users should override this value in such cases.
|
||||
function typeSize(idl: Idl, ty: IdlType): number {
|
||||
switch (ty) {
|
||||
case "bool":
|
||||
return 1;
|
||||
case "u8":
|
||||
return 1;
|
||||
case "i8":
|
||||
return 1;
|
||||
case "u16":
|
||||
return 2;
|
||||
case "u32":
|
||||
return 4;
|
||||
case "u64":
|
||||
return 8;
|
||||
case "i64":
|
||||
return 8;
|
||||
case "bytes":
|
||||
return 1;
|
||||
case "string":
|
||||
return 1;
|
||||
case "publicKey":
|
||||
return 32;
|
||||
default:
|
||||
// @ts-ignore
|
||||
if (ty.vec !== undefined) {
|
||||
return 1;
|
||||
}
|
||||
// @ts-ignore
|
||||
if (ty.option !== undefined) {
|
||||
// @ts-ignore
|
||||
return 1 + typeSize(ty.option);
|
||||
}
|
||||
// @ts-ignore
|
||||
if (ty.defined !== undefined) {
|
||||
// @ts-ignore
|
||||
const filtered = idl.types.filter((t) => t.name === ty.defined);
|
||||
if (filtered.length !== 1) {
|
||||
throw new IdlError(`Type not found: ${JSON.stringify(ty)}`);
|
||||
}
|
||||
let typeDef = filtered[0];
|
||||
// Returns the common keys that are prepended to all instructions targeting
|
||||
// the "state" of a program.
|
||||
async function stateInstructionKeys(
|
||||
programId: PublicKey,
|
||||
provider: Provider,
|
||||
m: IdlStateMethod,
|
||||
ctx: RpcContext
|
||||
) {
|
||||
if (m.name === "new") {
|
||||
// Ctor `new` method.
|
||||
const [programSigner, _nonce] = await PublicKey.findProgramAddress(
|
||||
[],
|
||||
programId
|
||||
);
|
||||
return [
|
||||
{
|
||||
pubkey: provider.wallet.publicKey,
|
||||
isWritable: false,
|
||||
isSigner: true,
|
||||
},
|
||||
{
|
||||
pubkey: await programStateAddress(programId),
|
||||
isWritable: true,
|
||||
isSigner: false,
|
||||
},
|
||||
{ pubkey: programSigner, isWritable: false, isSigner: false },
|
||||
{
|
||||
pubkey: SystemProgram.programId,
|
||||
isWritable: false,
|
||||
isSigner: false,
|
||||
},
|
||||
|
||||
return accountSize(idl, typeDef);
|
||||
}
|
||||
throw new Error(`Invalid type ${JSON.stringify(ty)}`);
|
||||
{ pubkey: programId, isWritable: false, isSigner: false },
|
||||
{
|
||||
pubkey: SYSVAR_RENT_PUBKEY,
|
||||
isWritable: false,
|
||||
isSigner: false,
|
||||
},
|
||||
];
|
||||
} else {
|
||||
validateAccounts(m.accounts, ctx.accounts);
|
||||
return [
|
||||
{
|
||||
pubkey: await programStateAddress(programId),
|
||||
isWritable: true,
|
||||
isSigner: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
import * as bs58 from "bs58";
|
||||
import { sha256 } from "crypto-hash";
|
||||
import { struct } from "superstruct";
|
||||
import assert from "assert";
|
||||
import { PublicKey, AccountInfo, Connection } from "@solana/web3.js";
|
||||
|
||||
export const TOKEN_PROGRAM_ID = new PublicKey(
|
||||
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
|
||||
);
|
||||
|
||||
async function getMultipleAccounts(
|
||||
connection: Connection,
|
||||
publicKeys: PublicKey[]
|
||||
): Promise<
|
||||
Array<null | { publicKey: PublicKey; account: AccountInfo<Buffer> }>
|
||||
> {
|
||||
const args = [publicKeys.map((k) => k.toBase58()), { commitment: "recent" }];
|
||||
// @ts-ignore
|
||||
const unsafeRes = await connection._rpcRequest("getMultipleAccounts", args);
|
||||
const res = GetMultipleAccountsAndContextRpcResult(unsafeRes);
|
||||
if (res.error) {
|
||||
throw new Error(
|
||||
"failed to get info about accounts " +
|
||||
publicKeys.map((k) => k.toBase58()).join(", ") +
|
||||
": " +
|
||||
res.error.message
|
||||
);
|
||||
}
|
||||
assert(typeof res.result !== "undefined");
|
||||
const accounts: Array<null | {
|
||||
executable: any;
|
||||
owner: PublicKey;
|
||||
lamports: any;
|
||||
data: Buffer;
|
||||
}> = [];
|
||||
for (const account of res.result.value) {
|
||||
let value: {
|
||||
executable: any;
|
||||
owner: PublicKey;
|
||||
lamports: any;
|
||||
data: Buffer;
|
||||
} | null = null;
|
||||
if (account === null) {
|
||||
accounts.push(null);
|
||||
continue;
|
||||
}
|
||||
if (res.result.value) {
|
||||
const { executable, owner, lamports, data } = account;
|
||||
assert(data[1] === "base64");
|
||||
value = {
|
||||
executable,
|
||||
owner: new PublicKey(owner),
|
||||
lamports,
|
||||
data: Buffer.from(data[0], "base64"),
|
||||
};
|
||||
}
|
||||
if (value === null) {
|
||||
throw new Error("Invalid response");
|
||||
}
|
||||
accounts.push(value);
|
||||
}
|
||||
return accounts.map((account, idx) => {
|
||||
if (account === null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
publicKey: publicKeys[idx],
|
||||
account,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function jsonRpcResult(resultDescription: any) {
|
||||
const jsonRpcVersion = struct.literal("2.0");
|
||||
return struct.union([
|
||||
struct({
|
||||
jsonrpc: jsonRpcVersion,
|
||||
id: "string",
|
||||
error: "any",
|
||||
}),
|
||||
struct({
|
||||
jsonrpc: jsonRpcVersion,
|
||||
id: "string",
|
||||
error: "null?",
|
||||
result: resultDescription,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
function jsonRpcResultAndContext(resultDescription: any) {
|
||||
return jsonRpcResult({
|
||||
context: struct({
|
||||
slot: "number",
|
||||
}),
|
||||
value: resultDescription,
|
||||
});
|
||||
}
|
||||
|
||||
const AccountInfoResult = struct({
|
||||
executable: "boolean",
|
||||
owner: "string",
|
||||
lamports: "number",
|
||||
data: "any",
|
||||
rentEpoch: "number?",
|
||||
});
|
||||
|
||||
const GetMultipleAccountsAndContextRpcResult = jsonRpcResultAndContext(
|
||||
struct.array([struct.union(["null", AccountInfoResult])])
|
||||
);
|
||||
|
||||
const utils = {
|
||||
bs58,
|
||||
sha256,
|
||||
getMultipleAccounts,
|
||||
};
|
||||
|
||||
export default utils;
|
|
@ -12,7 +12,8 @@ export default new Proxy({} as any, {
|
|||
const process = require("process");
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
throw new Error("`anchor.workspace` is not available in the browser");
|
||||
// Workspaces aren't available in the browser, yet.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!_populatedWorkspace) {
|
||||
|
|
192
ts/yarn.lock
192
ts/yarn.lock
|
@ -241,7 +241,7 @@
|
|||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.10.4"
|
||||
|
||||
"@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.3.1":
|
||||
"@babel/runtime@^7.11.2", "@babel/runtime@^7.3.1":
|
||||
version "7.12.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
|
||||
integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
|
||||
|
@ -665,35 +665,6 @@
|
|||
bn.js "^5.1.2"
|
||||
buffer-layout "^1.2.0"
|
||||
|
||||
"@project-serum/common@^0.0.1-beta.0":
|
||||
version "0.0.1-beta.0"
|
||||
resolved "https://registry.yarnpkg.com/@project-serum/common/-/common-0.0.1-beta.0.tgz#6a2ab2a0adbd19294048e5e96ad695445a959319"
|
||||
integrity sha512-qJVZIrWQtF24wAfyKnsYhqA698wOMCVEigv/bqpld1n2C7EV2srE7H4tLkTKJxWexeFQOzD4/VOiSfebtc72xQ==
|
||||
dependencies:
|
||||
"@project-serum/serum" "^0.13.17"
|
||||
bn.js "^5.1.2"
|
||||
superstruct "0.8.3"
|
||||
|
||||
"@project-serum/lockup@^0.0.1-beta.0":
|
||||
version "0.0.1-beta.0"
|
||||
resolved "https://registry.yarnpkg.com/@project-serum/lockup/-/lockup-0.0.1-beta.0.tgz#184ff0800d980c377ad00c75a09f3f50f42c30fc"
|
||||
integrity sha512-iSRQppQKidWax+XUJdFsY+P2hefTpwm61ucqM0yHPh4Wnm85s64jaPU531C+8m3lcSC24ynJ2MrP58VvD4Rzfg==
|
||||
dependencies:
|
||||
"@project-serum/borsh" "^0.0.1-beta.0"
|
||||
"@project-serum/common" "^0.0.1-beta.0"
|
||||
"@solana/spl-token" "0.0.11"
|
||||
bn.js "^5.1.2"
|
||||
buffer-layout "^1.2.0"
|
||||
|
||||
"@project-serum/serum@^0.13.17":
|
||||
version "0.13.17"
|
||||
resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.17.tgz#a2d506c87d094635ff66e4d918b8382d7e9aa78d"
|
||||
integrity sha512-Q4LMCALOXe/izvS5gICksYWOrrOZkzg2M3sxe4jOG5lrxmcCOQzCoISJ5u0t/LrnXpveJMU4BAVRmNOUBmSbLw==
|
||||
dependencies:
|
||||
"@solana/web3.js" "0.86.1"
|
||||
bn.js "^5.1.2"
|
||||
buffer-layout "^1.2.0"
|
||||
|
||||
"@sinonjs/commons@^1.7.0":
|
||||
version "1.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217"
|
||||
|
@ -708,27 +679,15 @@
|
|||
dependencies:
|
||||
"@sinonjs/commons" "^1.7.0"
|
||||
|
||||
"@solana/spl-token@0.0.11":
|
||||
version "0.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.0.11.tgz#5b978d38022beccaafac8b1dd1f3f575a39ebd1b"
|
||||
integrity sha512-Upp+x5B18UegyoDfDJFbkhtL5ynuJBe1AFCbhbSasp+fQP4xx9Lhq+w3rheZPPOnCYwgRfAodABwCO/j7ZFKPg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.10.5"
|
||||
"@solana/web3.js" "^0.78.0"
|
||||
bn.js "^5.0.0"
|
||||
buffer-layout "^1.2.0"
|
||||
dotenv "8.2.0"
|
||||
mkdirp "1.0.4"
|
||||
|
||||
"@solana/web3.js@0.86.1":
|
||||
version "0.86.1"
|
||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-0.86.1.tgz#034a2cef742569f74dfc9960dfbcabc92e674b08"
|
||||
integrity sha512-9mjWs17ym7PIm7bHA37wnnYyD7rIVHwkx1RI6BzGhMO5h8E+HlZM8ISLgOx+NItg8XRCfFhlrVgJTzK4om1s0g==
|
||||
"@solana/web3.js@^0.90.4":
|
||||
version "0.90.4"
|
||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-0.90.4.tgz#9fae2d1fcb5fb3acbcd74a4ac482f2f84b7f7056"
|
||||
integrity sha512-3HKXIcu+XXsXEFE58Yx6MZkbvSW1Pp0g9Hcvz2o0C5mawh7if2L7abgD5WNdXfIt3M4f6jM+H8fVSuoOJOZjrg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
bn.js "^5.0.0"
|
||||
bs58 "^4.0.1"
|
||||
buffer "^5.4.3"
|
||||
buffer "^6.0.1"
|
||||
buffer-layout "^1.2.0"
|
||||
crypto-hash "^1.2.2"
|
||||
esdoc-inject-style-plugin "^1.0.0"
|
||||
|
@ -743,27 +702,6 @@
|
|||
tweetnacl "^1.0.0"
|
||||
ws "^7.0.0"
|
||||
|
||||
"@solana/web3.js@^0.78.0":
|
||||
version "0.78.4"
|
||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-0.78.4.tgz#a942d02e353b2c3ff7ddc94cb6846b5a98887bc2"
|
||||
integrity sha512-Zt6LN35K2sQaQfgWbNjp91qGa6dHccXTQEoojXEo0NqZ/CQqmzretgSI/3kxKUiuvLTY/1WltVM7CKRkwMNRFA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
bn.js "^5.0.0"
|
||||
bs58 "^4.0.1"
|
||||
buffer "^5.4.3"
|
||||
buffer-layout "^1.2.0"
|
||||
crypto-hash "^1.2.2"
|
||||
esdoc-inject-style-plugin "^1.0.0"
|
||||
jayson "^3.0.1"
|
||||
mz "^2.7.0"
|
||||
node-fetch "^2.2.0"
|
||||
npm-run-all "^4.1.5"
|
||||
rpc-websockets "^7.4.2"
|
||||
superstruct "^0.8.3"
|
||||
tweetnacl "^1.0.0"
|
||||
ws "^7.0.0"
|
||||
|
||||
"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7":
|
||||
version "7.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.12.tgz#4d8e9e51eb265552a7e4f1ff2219ab6133bdfb2d"
|
||||
|
@ -804,6 +742,13 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/bs58@^4.0.1":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/bs58/-/bs58-4.0.1.tgz#3d51222aab067786d3bc3740a84a7f5a0effaa37"
|
||||
integrity sha512-yfAgiWgVLjFCmRv8zAcOIHywYATEwiTVccTLnRp6UxTNavT55M9d/uhK3T03St/+8/z/wW+CRjGKUNmEqoHHCA==
|
||||
dependencies:
|
||||
base-x "^3.0.6"
|
||||
|
||||
"@types/connect@^3.4.33":
|
||||
version "3.4.34"
|
||||
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.34.tgz#170a40223a6d666006d93ca128af2beb1d9b1901"
|
||||
|
@ -812,9 +757,9 @@
|
|||
"@types/node" "*"
|
||||
|
||||
"@types/express-serve-static-core@^4.17.9":
|
||||
version "4.17.17"
|
||||
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.17.tgz#6ba02465165b6c9c3d8db3a28def6b16fc9b70f5"
|
||||
integrity sha512-YYlVaCni5dnHc+bLZfY908IG1+x5xuibKZMGv8srKkvtul3wUuanYvpIj9GXXoWkQbaAdR+kgX46IETKUALWNQ==
|
||||
version "4.17.18"
|
||||
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.18.tgz#8371e260f40e0e1ca0c116a9afcd9426fa094c40"
|
||||
integrity sha512-m4JTwx5RUBNZvky/JJ8swEJPKFd8si08pPF2PfizYjGZOKr/svUWPcoUmLow6MmPzhasphB7gSTINY67xn3JNA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
"@types/qs" "*"
|
||||
|
@ -860,9 +805,9 @@
|
|||
integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==
|
||||
|
||||
"@types/lodash@^4.14.159":
|
||||
version "4.14.166"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.166.tgz#07e7f2699a149219dbc3c35574f126ec8737688f"
|
||||
integrity sha512-A3YT/c1oTlyvvW/GQqG86EyqWNrT/tisOIh2mW3YCgcx71TNjiTZA3zYZWA5BCmtsOTXjhliy4c4yEkErw6njA==
|
||||
version "4.14.168"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008"
|
||||
integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==
|
||||
|
||||
"@types/minimist@^1.2.0":
|
||||
version "1.2.1"
|
||||
|
@ -875,9 +820,9 @@
|
|||
integrity sha512-G0lD1/7qD60TJ/mZmhog76k7NcpLWkPVGgzkRy3CTlnFu4LUQh5v2Wa661z6vnXmD8EQrnALUyf0VRtrACYztw==
|
||||
|
||||
"@types/node@^12.12.54":
|
||||
version "12.19.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.11.tgz#9220ab4b20d91169eb78f456dbfcbabee89dfb50"
|
||||
integrity sha512-bwVfNTFZOrGXyiQ6t4B9sZerMSShWNsGRw8tC5DY1qImUNczS9SjT4G6PnzjCnxsu5Ubj6xjL2lgwddkxtQl5w==
|
||||
version "12.19.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.15.tgz#0de7e978fb43db62da369db18ea088a63673c182"
|
||||
integrity sha512-lowukE3GUI+VSYSu6VcBXl14d61Rp5hA1D+61r16qnwC0lYNSqdxcvRh0pswejorHfS+HgwBasM8jLXz0/aOsw==
|
||||
|
||||
"@types/normalize-package-data@^2.4.0":
|
||||
version "2.4.0"
|
||||
|
@ -1286,7 +1231,7 @@ balanced-match@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
base-x@^3.0.2:
|
||||
base-x@^3.0.2, base-x@^3.0.6:
|
||||
version "3.0.8"
|
||||
resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d"
|
||||
integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==
|
||||
|
@ -1405,18 +1350,18 @@ buffer-layout@^1.2.0:
|
|||
resolved "https://registry.yarnpkg.com/buffer-layout/-/buffer-layout-1.2.0.tgz#ee1f5ef05a8afd5db6b3a8fe2056c111bc69c737"
|
||||
integrity sha512-iiyRoho/ERzBUv6kFvfsrLNgTlVwOkqQcSQN7WrO3Y+c5SeuEhCn6+y1KwhM0V3ndptF5mI/RI44zkw0qcR5Jg==
|
||||
|
||||
buffer@^5.4.3:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
|
||||
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
|
||||
buffer@^6.0.1:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
|
||||
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
|
||||
dependencies:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.1.13"
|
||||
ieee754 "^1.2.1"
|
||||
|
||||
bufferutil@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.2.tgz#79f68631910f6b993d870fc77dc0a2894eb96cd5"
|
||||
integrity sha512-AtnG3W6M8B2n4xDQ5R+70EXvOpnXsFYg/AK2yTZd+HQ/oxAdz+GI+DvjmhBw3L0ole+LJ0ngqY4JMbDzkfNzhA==
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.3.tgz#66724b756bed23cd7c28c4d306d7994f9943cc6b"
|
||||
integrity sha512-yEYTwGndELGvfXsImMBLop58eaGW+YdONi1fNjTINSY98tmMmFijBG6WXgdkfuLNt4imzQNtIE+eBp1PVpMCSw==
|
||||
dependencies:
|
||||
node-gyp-build "^4.2.0"
|
||||
|
||||
|
@ -1435,13 +1380,13 @@ cache-base@^1.0.1:
|
|||
union-value "^1.0.0"
|
||||
unset-value "^1.0.0"
|
||||
|
||||
call-bind@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce"
|
||||
integrity sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==
|
||||
call-bind@^1.0.0, call-bind@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
|
||||
integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
get-intrinsic "^1.0.0"
|
||||
get-intrinsic "^1.0.2"
|
||||
|
||||
caller-callsite@^2.0.0:
|
||||
version "2.0.0"
|
||||
|
@ -2050,11 +1995,6 @@ dot-prop@^3.0.0:
|
|||
dependencies:
|
||||
is-obj "^1.0.0"
|
||||
|
||||
dotenv@8.2.0:
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
|
||||
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
|
||||
|
||||
ecc-jsbn@~0.1.1:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
|
||||
|
@ -2106,9 +2046,9 @@ entities@^1.1.1, entities@~1.1.1:
|
|||
integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
|
||||
|
||||
entities@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
|
||||
integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
|
||||
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
|
||||
|
||||
error-ex@^1.3.1:
|
||||
version "1.3.2"
|
||||
|
@ -2118,22 +2058,24 @@ error-ex@^1.3.1:
|
|||
is-arrayish "^0.2.1"
|
||||
|
||||
es-abstract@^1.18.0-next.1:
|
||||
version "1.18.0-next.1"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68"
|
||||
integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==
|
||||
version "1.18.0-next.2"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.2.tgz#088101a55f0541f595e7e057199e27ddc8f3a5c2"
|
||||
integrity sha512-Ih4ZMFHEtZupnUh6497zEL4y2+w8+1ljnCyaTa+adcoafI1GOvMwFlDjBLfWR7y9VLfrjRJe9ocuHY1PSR9jjw==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
es-to-primitive "^1.2.1"
|
||||
function-bind "^1.1.1"
|
||||
get-intrinsic "^1.0.2"
|
||||
has "^1.0.3"
|
||||
has-symbols "^1.0.1"
|
||||
is-callable "^1.2.2"
|
||||
is-negative-zero "^2.0.0"
|
||||
is-negative-zero "^2.0.1"
|
||||
is-regex "^1.1.1"
|
||||
object-inspect "^1.8.0"
|
||||
object-inspect "^1.9.0"
|
||||
object-keys "^1.1.1"
|
||||
object.assign "^4.1.1"
|
||||
string.prototype.trimend "^1.0.1"
|
||||
string.prototype.trimstart "^1.0.1"
|
||||
object.assign "^4.1.2"
|
||||
string.prototype.trimend "^1.0.3"
|
||||
string.prototype.trimstart "^1.0.3"
|
||||
|
||||
es-to-primitive@^1.2.1:
|
||||
version "1.2.1"
|
||||
|
@ -2600,10 +2542,10 @@ get-caller-file@^2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||
|
||||
get-intrinsic@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.2.tgz#6820da226e50b24894e08859469dc68361545d49"
|
||||
integrity sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==
|
||||
get-intrinsic@^1.0.2:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.0.tgz#892e62931e6938c8a23ea5aaebcfb67bd97da97e"
|
||||
integrity sha512-M11rgtQp5GZMZzDL7jLTNxbDfurpzuau5uqRWDPvlHjfvg3TdScAZo96GLvhMjImrmR8uAt0FS2RLoMrfWGKlg==
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
has "^1.0.3"
|
||||
|
@ -2892,7 +2834,7 @@ iconv-lite@0.4.24:
|
|||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3"
|
||||
|
||||
ieee754@^1.1.13:
|
||||
ieee754@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||
|
@ -3103,7 +3045,7 @@ is-glob@^4.0.0, is-glob@^4.0.1:
|
|||
dependencies:
|
||||
is-extglob "^2.1.1"
|
||||
|
||||
is-negative-zero@^2.0.0:
|
||||
is-negative-zero@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
|
||||
integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==
|
||||
|
@ -4249,7 +4191,7 @@ mixin-deep@^1.2.0:
|
|||
for-in "^1.0.2"
|
||||
is-extendable "^1.0.1"
|
||||
|
||||
mkdirp@1.0.4, mkdirp@1.x:
|
||||
mkdirp@1.x:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
|
@ -4436,7 +4378,7 @@ object-copy@^0.1.0:
|
|||
define-property "^0.2.5"
|
||||
kind-of "^3.0.3"
|
||||
|
||||
object-inspect@^1.8.0:
|
||||
object-inspect@^1.9.0:
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a"
|
||||
integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==
|
||||
|
@ -4453,7 +4395,7 @@ object-visit@^1.0.0:
|
|||
dependencies:
|
||||
isobject "^3.0.0"
|
||||
|
||||
object.assign@^4.1.1:
|
||||
object.assign@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
|
||||
integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==
|
||||
|
@ -5401,7 +5343,7 @@ string.prototype.padend@^3.0.0:
|
|||
define-properties "^1.1.3"
|
||||
es-abstract "^1.18.0-next.1"
|
||||
|
||||
string.prototype.trimend@^1.0.1:
|
||||
string.prototype.trimend@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz#a22bd53cca5c7cf44d7c9d5c732118873d6cd18b"
|
||||
integrity sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==
|
||||
|
@ -5409,7 +5351,7 @@ string.prototype.trimend@^1.0.1:
|
|||
call-bind "^1.0.0"
|
||||
define-properties "^1.1.3"
|
||||
|
||||
string.prototype.trimstart@^1.0.1:
|
||||
string.prototype.trimstart@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz#9b4cb590e123bb36564401d59824298de50fd5aa"
|
||||
integrity sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==
|
||||
|
@ -5484,14 +5426,6 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
|
|||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||
|
||||
superstruct@0.8.3:
|
||||
version "0.8.3"
|
||||
resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.8.3.tgz#fb4d8901aca3bf9f79afab1bbab7a7f335cc4ef2"
|
||||
integrity sha512-LbtbFpktW1FcwxVIJlxdk7bCyBq/GzOx2FSFLRLTUhWIA1gHkYPIl3aXRG5mBdGZtnPNT6t+4eEcLDCMOuBHww==
|
||||
dependencies:
|
||||
kind-of "^6.0.2"
|
||||
tiny-invariant "^1.0.6"
|
||||
|
||||
superstruct@^0.8.3:
|
||||
version "0.8.4"
|
||||
resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.8.4.tgz#478a19649f6b02c6319c02044db6a1f5863c391f"
|
||||
|
@ -5844,9 +5778,9 @@ use@^3.1.0:
|
|||
integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
|
||||
|
||||
utf-8-validate@^5.0.2:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.3.tgz#3b64e418ad2ff829809025fdfef595eab2f03a27"
|
||||
integrity sha512-jtJM6fpGv8C1SoH4PtG22pGto6x+Y8uPprW0tw3//gGFhDDTiuksgradgFN6yRayDP4SyZZa6ZMGHLIa17+M8A==
|
||||
version "5.0.4"
|
||||
resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.4.tgz#72a1735983ddf7a05a43a9c6b67c5ce1c910f9b8"
|
||||
integrity sha512-MEF05cPSq3AwJ2C7B7sHAA6i53vONoZbMGX8My5auEVm6W+dJ2Jd/TZPyGJ5CH42V2XtbI5FD28HeHeqlPzZ3Q==
|
||||
dependencies:
|
||||
node-gyp-build "^4.2.0"
|
||||
|
||||
|
|
Loading…
Reference in New Issue