Add support for Solidity using the Solang compiler (#2421)
anchor init and anchor new have a new option `-s` (or `--solidity`) which creates an example using Solidity in the soldity directory of the anchor workspace. anchor deploy/build/test work accordingly. solang is required to be in the path, this can be downloaded from the release at https://github.com/hyperledger/solang anchor build/deploy finds all the solidity contracts by using the solidity parser to find all contract declarations.
This commit is contained in:
parent
fbd0688903
commit
9e7c33eec1
|
@ -13,6 +13,7 @@ The minor version will be incremented upon a breaking change and the patch versi
|
|||
### Features
|
||||
|
||||
- spl: Add metadata wrappers `approve_collection_authority`, `bubblegum_set_collection_size`, `burn_edition_nft`, `burn_nft`, `revoke_collection_authority`, `set_token_standard`, `utilize`, `unverify_sized_collection_item`, `unverify_collection` ([#2430](https://github.com/coral-xyz/anchor/pull/2430))
|
||||
- cli: Add support for Solidity programs. `anchor init` and `anchor new` take an option `--solidity` which creates solidity code rather than rust. `anchor build` and `anchor test` work accordingly ([#2421](https://github.com/coral-xyz/anchor/pull/2421))
|
||||
|
||||
### Fixes
|
||||
- ts: Narrowed `AccountClient` type to it's appropriate account type ([#2440](https://github.com/coral-xyz/anchor/pull/2440))
|
||||
|
|
|
@ -187,6 +187,7 @@ dependencies = [
|
|||
"solana-faucet",
|
||||
"solana-program",
|
||||
"solana-sdk",
|
||||
"solang-parser",
|
||||
"syn 1.0.109",
|
||||
"tar",
|
||||
"tokio",
|
||||
|
@ -413,6 +414,15 @@ version = "0.9.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e"
|
||||
|
||||
[[package]]
|
||||
name = "ascii-canvas"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6"
|
||||
dependencies = [
|
||||
"term",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asn1-rs"
|
||||
version = "0.5.1"
|
||||
|
@ -498,7 +508,7 @@ version = "0.2.14"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.1.19",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
@ -536,6 +546,21 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
@ -1194,6 +1219,12 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
|
@ -1336,6 +1367,15 @@ version = "1.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||
|
||||
[[package]]
|
||||
name = "ena"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
|
@ -1416,6 +1456,27 @@ dependencies = [
|
|||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno-dragonfly"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.5.3"
|
||||
|
@ -1459,6 +1520,12 @@ dependencies = [
|
|||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.24"
|
||||
|
@ -1701,6 +1768,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "histogram"
|
||||
version = "0.6.9"
|
||||
|
@ -1910,12 +1986,34 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
|
||||
dependencies = [
|
||||
"hermit-abi 0.2.6",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.9.0"
|
||||
|
@ -1979,6 +2077,38 @@ version = "0.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838"
|
||||
|
||||
[[package]]
|
||||
name = "lalrpop"
|
||||
version = "0.19.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f34313ec00c2eb5c3c87ca6732ea02dcf3af99c3ff7a8fb622ffb99c9d860a87"
|
||||
dependencies = [
|
||||
"ascii-canvas",
|
||||
"bit-set",
|
||||
"diff",
|
||||
"ena",
|
||||
"is-terminal",
|
||||
"itertools 0.10.5",
|
||||
"lalrpop-util",
|
||||
"petgraph",
|
||||
"pico-args",
|
||||
"regex",
|
||||
"regex-syntax",
|
||||
"string_cache",
|
||||
"term",
|
||||
"tiny-keccak",
|
||||
"unicode-xid 0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lalrpop-util"
|
||||
version = "0.19.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5c1f7869c94d214466c5fd432dfed12c379fd87786768d36455892d46b18edd"
|
||||
dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
|
@ -2058,6 +2188,12 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.9"
|
||||
|
@ -2225,6 +2361,12 @@ dependencies = [
|
|||
"spl-token",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "new_debug_unreachable"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.25.1"
|
||||
|
@ -2354,7 +2496,7 @@ version = "1.13.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.1.19",
|
||||
"libc",
|
||||
]
|
||||
|
||||
|
@ -2514,6 +2656,73 @@ dependencies = [
|
|||
"ucd-trie",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
|
||||
dependencies = [
|
||||
"phf_macros",
|
||||
"phf_shared 0.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf"
|
||||
dependencies = [
|
||||
"phf_shared 0.11.1",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared 0.11.1",
|
||||
"proc-macro2 1.0.47",
|
||||
"quote 1.0.21",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.9"
|
||||
|
@ -2576,6 +2785,12 @@ version = "0.2.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||
|
||||
[[package]]
|
||||
name = "precomputed-hash"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "0.1.5"
|
||||
|
@ -3016,6 +3231,20 @@ dependencies = [
|
|||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.20.7"
|
||||
|
@ -3400,6 +3629,12 @@ version = "1.6.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||
|
||||
[[package]]
|
||||
name = "sized-chunks"
|
||||
version = "0.6.5"
|
||||
|
@ -4232,6 +4467,19 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solang-parser"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff87dae6cdccacdbf3b19e99b271083556e808de0f59c74a01482f64fdbc61fc"
|
||||
dependencies = [
|
||||
"itertools 0.10.5",
|
||||
"lalrpop",
|
||||
"lalrpop-util",
|
||||
"phf",
|
||||
"unicode-xid 0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
|
@ -4312,6 +4560,19 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "string_cache"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
|
||||
dependencies = [
|
||||
"new_debug_unreachable",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"phf_shared 0.10.0",
|
||||
"precomputed-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
|
@ -4389,6 +4650,17 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
|
||||
dependencies = [
|
||||
"dirs-next",
|
||||
"rustversion",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
|
@ -4502,6 +4774,15 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
|
|
|
@ -26,6 +26,7 @@ anchor-syn = { path = "../lang/syn", features = ["idl", "init-if-needed"], versi
|
|||
serde_json = "1.0"
|
||||
shellexpand = "2.1.0"
|
||||
toml = "0.5.8"
|
||||
solang-parser = "=0.2.3"
|
||||
semver = "1.0.4"
|
||||
serde = { version = "1.0.122", features = ["derive"] }
|
||||
solana-sdk = "1.14.16"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::is_hidden;
|
||||
use anchor_client::Cluster;
|
||||
use anchor_syn::idl::Idl;
|
||||
use anyhow::{anyhow, Context, Error, Result};
|
||||
use anyhow::{anyhow, bail, Context, Error, Result};
|
||||
use clap::{Parser, ValueEnum};
|
||||
use heck::ToSnakeCase;
|
||||
use reqwest::Url;
|
||||
|
@ -10,8 +10,10 @@ use serde::{Deserialize, Deserializer, Serialize};
|
|||
use solana_cli_config::{Config as SolanaConfig, CONFIG_FILE};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solang_parser::pt::{ContractTy, SourceUnitPart};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::convert::TryFrom;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::{self, File};
|
||||
use std::io::prelude::*;
|
||||
use std::marker::PhantomData;
|
||||
|
@ -145,7 +147,7 @@ impl Deref for Manifest {
|
|||
}
|
||||
|
||||
impl WithPath<Config> {
|
||||
pub fn get_program_list(&self) -> Result<Vec<PathBuf>> {
|
||||
pub fn get_rust_program_list(&self) -> Result<Vec<PathBuf>> {
|
||||
// Canonicalize the workspace filepaths to compare with relative paths.
|
||||
let (members, exclude) = self.canonicalize_workspace()?;
|
||||
|
||||
|
@ -156,12 +158,16 @@ impl WithPath<Config> {
|
|||
let program_paths: Vec<PathBuf> = {
|
||||
if members.is_empty() {
|
||||
let path = self.path().parent().unwrap().join("programs");
|
||||
fs::read_dir(path)?
|
||||
.filter(|entry| entry.as_ref().map(|e| e.path().is_dir()).unwrap_or(false))
|
||||
.map(|dir| dir.map(|d| d.path().canonicalize().unwrap()))
|
||||
.collect::<Vec<Result<PathBuf, std::io::Error>>>()
|
||||
.into_iter()
|
||||
.collect::<Result<Vec<PathBuf>, std::io::Error>>()?
|
||||
if let Ok(entries) = fs::read_dir(path) {
|
||||
entries
|
||||
.filter(|entry| entry.as_ref().map(|e| e.path().is_dir()).unwrap_or(false))
|
||||
.map(|dir| dir.map(|d| d.path().canonicalize().unwrap()))
|
||||
.collect::<Vec<Result<PathBuf, std::io::Error>>>()
|
||||
.into_iter()
|
||||
.collect::<Result<Vec<PathBuf>, std::io::Error>>()?
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
} else {
|
||||
members
|
||||
}
|
||||
|
@ -174,9 +180,56 @@ impl WithPath<Config> {
|
|||
.collect())
|
||||
}
|
||||
|
||||
/// Parse all the files with the .sol extension, and get a list of the all
|
||||
/// contracts defined in them along with their path. One Solidity file may
|
||||
/// define multiple contracts.
|
||||
pub fn get_solidity_program_list(&self) -> Result<Vec<(String, PathBuf)>> {
|
||||
let path = self.path().parent().unwrap().join("solidity");
|
||||
let mut res = Vec::new();
|
||||
|
||||
if let Ok(entries) = fs::read_dir(path) {
|
||||
for entry in entries {
|
||||
let path = entry?.path();
|
||||
|
||||
if !path.is_file() || path.extension() != Some(OsStr::new("sol")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let source = fs::read_to_string(&path)?;
|
||||
|
||||
let tree = match solang_parser::parse(&source, 0) {
|
||||
Ok((tree, _)) => tree,
|
||||
Err(diag) => {
|
||||
// The parser can return multiple errors, however this is exceedingly rare.
|
||||
// Just use the first one, else the formatting will be a mess.
|
||||
bail!(
|
||||
"{}: {}: {}",
|
||||
path.display(),
|
||||
diag[0].level.to_string(),
|
||||
diag[0].message
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
tree.0.iter().for_each(|part| {
|
||||
if let SourceUnitPart::ContractDefinition(contract) = part {
|
||||
// Must be a contract, not library/interface/abstract contract
|
||||
if matches!(&contract.ty, ContractTy::Contract(..)) {
|
||||
if let Some(name) = &contract.name {
|
||||
res.push((name.name.clone(), path.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn read_all_programs(&self) -> Result<Vec<Program>> {
|
||||
let mut r = vec![];
|
||||
for path in self.get_program_list()? {
|
||||
for path in self.get_rust_program_list()? {
|
||||
let cargo = Manifest::from_path(path.join("Cargo.toml"))?;
|
||||
let lib_name = cargo.lib_name()?;
|
||||
|
||||
|
@ -188,6 +241,21 @@ impl WithPath<Config> {
|
|||
|
||||
r.push(Program {
|
||||
lib_name,
|
||||
solidity: false,
|
||||
path,
|
||||
idl,
|
||||
});
|
||||
}
|
||||
for (lib_name, path) in self.get_solidity_program_list()? {
|
||||
let idl_filepath = format!("target/idl/{lib_name}.json");
|
||||
let idl = fs::read(idl_filepath)
|
||||
.ok()
|
||||
.map(|bytes| serde_json::from_reader(&*bytes))
|
||||
.transpose()?;
|
||||
|
||||
r.push(Program {
|
||||
lib_name,
|
||||
solidity: true,
|
||||
path,
|
||||
idl,
|
||||
});
|
||||
|
@ -1126,7 +1194,8 @@ impl Merge for _Validator {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct Program {
|
||||
pub lib_name: String,
|
||||
// Canonicalized path to the program directory.
|
||||
pub solidity: bool,
|
||||
// Canonicalized path to the program directory or Solidity source file
|
||||
pub path: PathBuf,
|
||||
pub idl: Option<Idl>,
|
||||
}
|
||||
|
|
279
cli/src/lib.rs
279
cli/src/lib.rs
|
@ -46,7 +46,8 @@ use tar::Archive;
|
|||
|
||||
pub mod config;
|
||||
mod path;
|
||||
pub mod template;
|
||||
pub mod rust_template;
|
||||
pub mod solidity_template;
|
||||
|
||||
// Version of the docker image.
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
@ -68,6 +69,8 @@ pub enum Command {
|
|||
name: String,
|
||||
#[clap(short, long)]
|
||||
javascript: bool,
|
||||
#[clap(short, long)]
|
||||
solidity: bool,
|
||||
#[clap(long)]
|
||||
no_git: bool,
|
||||
#[clap(long)]
|
||||
|
@ -200,7 +203,11 @@ pub enum Command {
|
|||
cargo_args: Vec<String>,
|
||||
},
|
||||
/// Creates a new program.
|
||||
New { name: String },
|
||||
New {
|
||||
#[clap(short, long)]
|
||||
solidity: bool,
|
||||
name: String,
|
||||
},
|
||||
/// Commands for interacting with interface definitions.
|
||||
Idl {
|
||||
#[clap(subcommand)]
|
||||
|
@ -214,7 +221,7 @@ pub enum Command {
|
|||
#[clap(short, long)]
|
||||
program_name: Option<String>,
|
||||
/// Keypair of the program (filepath) (requires program-name)
|
||||
#[clap(long, requires = "program-name")]
|
||||
#[clap(long, requires = "program_name")]
|
||||
program_keypair: Option<String>,
|
||||
},
|
||||
/// Runs the deploy migration script.
|
||||
|
@ -412,10 +419,11 @@ pub fn entry(opts: Opts) -> Result<()> {
|
|||
Command::Init {
|
||||
name,
|
||||
javascript,
|
||||
solidity,
|
||||
no_git,
|
||||
jest,
|
||||
} => init(&opts.cfg_override, name, javascript, no_git, jest),
|
||||
Command::New { name } => new(&opts.cfg_override, name),
|
||||
} => init(&opts.cfg_override, name, javascript, solidity, no_git, jest),
|
||||
Command::New { solidity, name } => new(&opts.cfg_override, solidity, name),
|
||||
Command::Build {
|
||||
idl,
|
||||
idl_ts,
|
||||
|
@ -559,6 +567,7 @@ fn init(
|
|||
cfg_override: &ConfigOverride,
|
||||
name: String,
|
||||
javascript: bool,
|
||||
solidity: bool,
|
||||
no_git: bool,
|
||||
jest: bool,
|
||||
) -> Result<()> {
|
||||
|
@ -617,7 +626,11 @@ fn init(
|
|||
localnet.insert(
|
||||
rust_name,
|
||||
ProgramDeployment {
|
||||
address: template::default_program_id(),
|
||||
address: if solidity {
|
||||
solidity_template::default_program_id()
|
||||
} else {
|
||||
rust_template::default_program_id()
|
||||
},
|
||||
path: None,
|
||||
idl: None,
|
||||
},
|
||||
|
@ -626,20 +639,25 @@ fn init(
|
|||
let toml = cfg.to_string();
|
||||
fs::write("Anchor.toml", toml)?;
|
||||
|
||||
// Build virtual manifest.
|
||||
fs::write("Cargo.toml", template::virtual_manifest())?;
|
||||
|
||||
// Initialize .gitignore file
|
||||
fs::write(".gitignore", template::git_ignore())?;
|
||||
fs::write(".gitignore", rust_template::git_ignore())?;
|
||||
|
||||
// Initialize .prettierignore file
|
||||
fs::write(".prettierignore", template::prettier_ignore())?;
|
||||
fs::write(".prettierignore", rust_template::prettier_ignore())?;
|
||||
|
||||
// Build the program.
|
||||
fs::create_dir("programs")?;
|
||||
if solidity {
|
||||
fs::create_dir("solidity")?;
|
||||
|
||||
new_program(&project_name)?;
|
||||
new_solidity_program(&project_name)?;
|
||||
} else {
|
||||
// Build virtual manifest for rust programs
|
||||
fs::write("Cargo.toml", rust_template::virtual_manifest())?;
|
||||
|
||||
fs::create_dir("programs")?;
|
||||
|
||||
new_rust_program(&project_name)?;
|
||||
}
|
||||
// Build the test suite.
|
||||
fs::create_dir("tests")?;
|
||||
// Build the migrations directory.
|
||||
|
@ -648,31 +666,44 @@ fn init(
|
|||
if javascript {
|
||||
// Build javascript config
|
||||
let mut package_json = File::create("package.json")?;
|
||||
package_json.write_all(template::package_json(jest).as_bytes())?;
|
||||
package_json.write_all(rust_template::package_json(jest).as_bytes())?;
|
||||
|
||||
if jest {
|
||||
let mut test = File::create(format!("tests/{}.test.js", &project_name))?;
|
||||
test.write_all(template::jest(&project_name).as_bytes())?;
|
||||
if solidity {
|
||||
test.write_all(solidity_template::jest(&project_name).as_bytes())?;
|
||||
} else {
|
||||
test.write_all(rust_template::jest(&project_name).as_bytes())?;
|
||||
}
|
||||
} else {
|
||||
let mut test = File::create(format!("tests/{}.js", &project_name))?;
|
||||
test.write_all(template::mocha(&project_name).as_bytes())?;
|
||||
if solidity {
|
||||
test.write_all(solidity_template::mocha(&project_name).as_bytes())?;
|
||||
} else {
|
||||
test.write_all(rust_template::mocha(&project_name).as_bytes())?;
|
||||
}
|
||||
}
|
||||
|
||||
let mut deploy = File::create("migrations/deploy.js")?;
|
||||
deploy.write_all(template::deploy_script().as_bytes())?;
|
||||
|
||||
deploy.write_all(rust_template::deploy_script().as_bytes())?;
|
||||
} else {
|
||||
// Build typescript config
|
||||
let mut ts_config = File::create("tsconfig.json")?;
|
||||
ts_config.write_all(template::ts_config(jest).as_bytes())?;
|
||||
ts_config.write_all(rust_template::ts_config(jest).as_bytes())?;
|
||||
|
||||
let mut ts_package_json = File::create("package.json")?;
|
||||
ts_package_json.write_all(template::ts_package_json(jest).as_bytes())?;
|
||||
ts_package_json.write_all(rust_template::ts_package_json(jest).as_bytes())?;
|
||||
|
||||
let mut deploy = File::create("migrations/deploy.ts")?;
|
||||
deploy.write_all(template::ts_deploy_script().as_bytes())?;
|
||||
deploy.write_all(rust_template::ts_deploy_script().as_bytes())?;
|
||||
|
||||
let mut mocha = File::create(format!("tests/{}.ts", &project_name))?;
|
||||
mocha.write_all(template::ts_mocha(&project_name).as_bytes())?;
|
||||
if solidity {
|
||||
mocha.write_all(solidity_template::ts_mocha(&project_name).as_bytes())?;
|
||||
} else {
|
||||
mocha.write_all(rust_template::ts_mocha(&project_name).as_bytes())?;
|
||||
}
|
||||
}
|
||||
|
||||
let yarn_result = install_node_modules("yarn")?;
|
||||
|
@ -717,7 +748,7 @@ fn install_node_modules(cmd: &str) -> Result<std::process::Output> {
|
|||
}
|
||||
|
||||
// Creates a new program crate in the `programs/<name>` directory.
|
||||
fn new(cfg_override: &ConfigOverride, name: String) -> Result<()> {
|
||||
fn new(cfg_override: &ConfigOverride, solidity: bool, name: String) -> Result<()> {
|
||||
with_workspace(cfg_override, |cfg| {
|
||||
match cfg.path().parent() {
|
||||
None => {
|
||||
|
@ -725,7 +756,11 @@ fn new(cfg_override: &ConfigOverride, name: String) -> Result<()> {
|
|||
}
|
||||
Some(parent) => {
|
||||
std::env::set_current_dir(parent)?;
|
||||
new_program(&name)?;
|
||||
if solidity {
|
||||
new_solidity_program(&name)?;
|
||||
} else {
|
||||
new_rust_program(&name)?;
|
||||
}
|
||||
println!("Created new program.");
|
||||
}
|
||||
};
|
||||
|
@ -733,16 +768,26 @@ fn new(cfg_override: &ConfigOverride, name: String) -> Result<()> {
|
|||
})
|
||||
}
|
||||
|
||||
// Creates a new program crate in the current directory with `name`.
|
||||
fn new_program(name: &str) -> Result<()> {
|
||||
fs::create_dir(format!("programs/{name}"))?;
|
||||
fs::create_dir(format!("programs/{name}/src/"))?;
|
||||
// Creates a new rust program crate in the current directory with `name`.
|
||||
fn new_rust_program(name: &str) -> Result<()> {
|
||||
if !PathBuf::from("Cargo.toml").exists() {
|
||||
fs::write("Cargo.toml", rust_template::virtual_manifest())?;
|
||||
}
|
||||
fs::create_dir_all(format!("programs/{name}/src/"))?;
|
||||
let mut cargo_toml = File::create(format!("programs/{name}/Cargo.toml"))?;
|
||||
cargo_toml.write_all(template::cargo_toml(name).as_bytes())?;
|
||||
cargo_toml.write_all(rust_template::cargo_toml(name).as_bytes())?;
|
||||
let mut xargo_toml = File::create(format!("programs/{name}/Xargo.toml"))?;
|
||||
xargo_toml.write_all(template::xargo_toml().as_bytes())?;
|
||||
xargo_toml.write_all(rust_template::xargo_toml().as_bytes())?;
|
||||
let mut lib_rs = File::create(format!("programs/{name}/src/lib.rs"))?;
|
||||
lib_rs.write_all(template::lib_rs(name).as_bytes())?;
|
||||
lib_rs.write_all(rust_template::lib_rs(name).as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Creates a new solidity program in the current directory with `name`.
|
||||
fn new_solidity_program(name: &str) -> Result<()> {
|
||||
fs::create_dir_all("solidity")?;
|
||||
let mut lib_rs = File::create(format!("solidity/{name}.sol"))?;
|
||||
lib_rs.write_all(solidity_template::solidity(name).as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -786,7 +831,7 @@ fn expand_all(
|
|||
cargo_args: &[String],
|
||||
) -> Result<()> {
|
||||
let cur_dir = std::env::current_dir()?;
|
||||
for p in workspace_cfg.get_program_list()? {
|
||||
for p in workspace_cfg.get_rust_program_list()? {
|
||||
expand_program(p, expansions_path.clone(), cargo_args)?;
|
||||
}
|
||||
std::env::set_current_dir(cur_dir)?;
|
||||
|
@ -923,7 +968,7 @@ pub fn build(
|
|||
arch,
|
||||
)?,
|
||||
// Cargo.toml represents a single package. Build it.
|
||||
Some(cargo) => build_cwd(
|
||||
Some(cargo) => build_rust_cwd(
|
||||
&cfg,
|
||||
cargo.path().to_path_buf(),
|
||||
idl_out,
|
||||
|
@ -963,8 +1008,8 @@ fn build_all(
|
|||
let r = match cfg_path.parent() {
|
||||
None => Err(anyhow!("Invalid Anchor.toml at {}", cfg_path.display())),
|
||||
Some(_parent) => {
|
||||
for p in cfg.get_program_list()? {
|
||||
build_cwd(
|
||||
for p in cfg.get_rust_program_list()? {
|
||||
build_rust_cwd(
|
||||
cfg,
|
||||
p.join("Cargo.toml"),
|
||||
idl_out.clone(),
|
||||
|
@ -979,6 +1024,19 @@ fn build_all(
|
|||
&arch,
|
||||
)?;
|
||||
}
|
||||
for (name, path) in cfg.get_solidity_program_list()? {
|
||||
build_solidity_cwd(
|
||||
cfg,
|
||||
name,
|
||||
path,
|
||||
idl_out.clone(),
|
||||
idl_ts_out.clone(),
|
||||
build_config,
|
||||
stdout.as_ref().map(|f| f.try_clone()).transpose()?,
|
||||
stderr.as_ref().map(|f| f.try_clone()).transpose()?,
|
||||
cargo_args.clone(),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
@ -988,7 +1046,7 @@ fn build_all(
|
|||
|
||||
// Runs the build command outside of a workspace.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn build_cwd(
|
||||
fn build_rust_cwd(
|
||||
cfg: &WithPath<Config>,
|
||||
cargo_toml: PathBuf,
|
||||
idl_out: Option<PathBuf>,
|
||||
|
@ -1007,7 +1065,7 @@ fn build_cwd(
|
|||
Some(p) => std::env::set_current_dir(p)?,
|
||||
};
|
||||
match build_config.verifiable {
|
||||
false => _build_cwd(cfg, idl_out, idl_ts_out, skip_lint, arch, cargo_args),
|
||||
false => _build_rust_cwd(cfg, idl_out, idl_ts_out, skip_lint, arch, cargo_args),
|
||||
true => build_cwd_verifiable(
|
||||
cfg,
|
||||
cargo_toml,
|
||||
|
@ -1023,6 +1081,31 @@ fn build_cwd(
|
|||
}
|
||||
}
|
||||
|
||||
// Runs the build command outside of a workspace.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn build_solidity_cwd(
|
||||
cfg: &WithPath<Config>,
|
||||
name: String,
|
||||
path: PathBuf,
|
||||
idl_out: Option<PathBuf>,
|
||||
idl_ts_out: Option<PathBuf>,
|
||||
build_config: &BuildConfig,
|
||||
stdout: Option<File>,
|
||||
stderr: Option<File>,
|
||||
cargo_args: Vec<String>,
|
||||
) -> Result<()> {
|
||||
match path.parent() {
|
||||
None => return Err(anyhow!("Unable to find parent")),
|
||||
Some(p) => std::env::set_current_dir(p)?,
|
||||
};
|
||||
match build_config.verifiable {
|
||||
false => _build_solidity_cwd(
|
||||
cfg, &name, &path, idl_out, idl_ts_out, stdout, stderr, cargo_args,
|
||||
),
|
||||
true => panic!("verifiable solidity not supported"),
|
||||
}
|
||||
}
|
||||
|
||||
// Builds an anchor program in a docker image and copies the build artifacts
|
||||
// into the `target/` directory.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -1078,7 +1161,7 @@ fn build_cwd_verifiable(
|
|||
// Write out the TypeScript type.
|
||||
println!("Writing the .ts file");
|
||||
let ts_file = workspace_dir.join(format!("target/types/{}.ts", idl.name));
|
||||
fs::write(&ts_file, template::idl_ts(&idl)?)?;
|
||||
fs::write(&ts_file, rust_template::idl_ts(&idl)?)?;
|
||||
|
||||
// Copy out the TypeScript type.
|
||||
if !&cfg.workspace.types.is_empty() {
|
||||
|
@ -1341,7 +1424,7 @@ fn docker_exec(container_name: &str, args: &[&str]) -> Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
fn _build_cwd(
|
||||
fn _build_rust_cwd(
|
||||
cfg: &WithPath<Config>,
|
||||
idl_out: Option<PathBuf>,
|
||||
idl_ts_out: Option<PathBuf>,
|
||||
|
@ -1377,7 +1460,7 @@ fn _build_cwd(
|
|||
// Write out the JSON file.
|
||||
write_idl(&idl, OutFile::File(out))?;
|
||||
// Write out the TypeScript type.
|
||||
fs::write(&ts_out, template::idl_ts(&idl)?)?;
|
||||
fs::write(&ts_out, rust_template::idl_ts(&idl)?)?;
|
||||
// Copy out the TypeScript type.
|
||||
let cfg_parent = cfg.path().parent().expect("Invalid Anchor.toml");
|
||||
if !&cfg.workspace.types.is_empty() {
|
||||
|
@ -1394,6 +1477,80 @@ fn _build_cwd(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn _build_solidity_cwd(
|
||||
cfg: &WithPath<Config>,
|
||||
name: &str,
|
||||
path: &Path,
|
||||
idl_out: Option<PathBuf>,
|
||||
idl_ts_out: Option<PathBuf>,
|
||||
stdout: Option<File>,
|
||||
stderr: Option<File>,
|
||||
solang_args: Vec<String>,
|
||||
) -> Result<()> {
|
||||
let mut cmd = std::process::Command::new("solang");
|
||||
let cmd = cmd.args(["compile", "--target", "solana", "--contract", name]);
|
||||
|
||||
if let Some(idl_out) = &idl_out {
|
||||
cmd.arg("--output-meta");
|
||||
cmd.arg(idl_out);
|
||||
}
|
||||
|
||||
let target_bin = cfg.path().parent().unwrap().join("target").join("deploy");
|
||||
|
||||
cmd.arg("--output");
|
||||
cmd.arg(target_bin);
|
||||
cmd.arg("--verbose");
|
||||
cmd.arg(path);
|
||||
|
||||
let exit = cmd
|
||||
.args(solang_args)
|
||||
.stdout(match stdout {
|
||||
None => Stdio::inherit(),
|
||||
Some(f) => f.into(),
|
||||
})
|
||||
.stderr(match stderr {
|
||||
None => Stdio::inherit(),
|
||||
Some(f) => f.into(),
|
||||
})
|
||||
.output()
|
||||
.map_err(|e| anyhow::format_err!("{}", e.to_string()))?;
|
||||
if !exit.status.success() {
|
||||
std::process::exit(exit.status.code().unwrap_or(1));
|
||||
}
|
||||
|
||||
// idl is written to idl_out or .
|
||||
let idl_path = idl_out
|
||||
.unwrap_or(PathBuf::from("."))
|
||||
.join(format!("{}.json", name));
|
||||
|
||||
let idl = fs::read_to_string(idl_path)?;
|
||||
|
||||
let idl: Idl = serde_json::from_str(&idl)?;
|
||||
|
||||
// TS out path.
|
||||
let ts_out = match idl_ts_out {
|
||||
None => PathBuf::from(".").join(&idl.name).with_extension("ts"),
|
||||
Some(o) => PathBuf::from(&o.join(&idl.name).with_extension("ts")),
|
||||
};
|
||||
|
||||
// Write out the TypeScript type.
|
||||
fs::write(&ts_out, rust_template::idl_ts(&idl)?)?;
|
||||
// Copy out the TypeScript type.
|
||||
let cfg_parent = cfg.path().parent().expect("Invalid Anchor.toml");
|
||||
if !&cfg.workspace.types.is_empty() {
|
||||
fs::copy(
|
||||
&ts_out,
|
||||
cfg_parent
|
||||
.join(&cfg.workspace.types)
|
||||
.join(&idl.name)
|
||||
.with_extension("ts"),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn verify(
|
||||
cfg_override: &ConfigOverride,
|
||||
|
@ -1476,17 +1633,24 @@ fn cd_member(cfg_override: &ConfigOverride, program_name: &str) -> Result<()> {
|
|||
let cfg = Config::discover(cfg_override)?.expect("Not in workspace.");
|
||||
|
||||
for program in cfg.read_all_programs()? {
|
||||
let cargo_toml = program.path.join("Cargo.toml");
|
||||
if !cargo_toml.exists() {
|
||||
return Err(anyhow!(
|
||||
"Did not find Cargo.toml at the path: {}",
|
||||
program.path.display()
|
||||
));
|
||||
}
|
||||
let p_lib_name = Manifest::from_path(&cargo_toml)?.lib_name()?;
|
||||
if program_name == p_lib_name {
|
||||
std::env::set_current_dir(&program.path)?;
|
||||
return Ok(());
|
||||
if program.solidity {
|
||||
if let Some(path) = program.path.parent() {
|
||||
std::env::set_current_dir(path)?;
|
||||
return Ok(());
|
||||
}
|
||||
} else {
|
||||
let cargo_toml = program.path.join("Cargo.toml");
|
||||
if !cargo_toml.exists() {
|
||||
return Err(anyhow!(
|
||||
"Did not find Cargo.toml at the path: {}",
|
||||
program.path.display()
|
||||
));
|
||||
}
|
||||
let p_lib_name = Manifest::from_path(&cargo_toml)?.lib_name()?;
|
||||
if program_name == p_lib_name {
|
||||
std::env::set_current_dir(&program.path)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(anyhow!("{} is not part of the workspace", program_name,))
|
||||
|
@ -2007,7 +2171,7 @@ fn idl_parse(
|
|||
|
||||
// Write out the TypeScript IDL.
|
||||
if let Some(out) = out_ts {
|
||||
fs::write(out, template::idl_ts(&idl)?)?;
|
||||
fs::write(out, rust_template::idl_ts(&idl)?)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -3116,7 +3280,7 @@ fn migrate(cfg_override: &ConfigOverride) -> Result<()> {
|
|||
let exit = if use_ts {
|
||||
let module_path = cur_dir.join("migrations/deploy.ts");
|
||||
let deploy_script_host_str =
|
||||
template::deploy_ts_script_host(&url, &module_path.display().to_string());
|
||||
rust_template::deploy_ts_script_host(&url, &module_path.display().to_string());
|
||||
fs::write("deploy.ts", deploy_script_host_str)?;
|
||||
std::process::Command::new("ts-node")
|
||||
.arg("deploy.ts")
|
||||
|
@ -3127,7 +3291,7 @@ fn migrate(cfg_override: &ConfigOverride) -> Result<()> {
|
|||
} else {
|
||||
let module_path = cur_dir.join("migrations/deploy.js");
|
||||
let deploy_script_host_str =
|
||||
template::deploy_js_script_host(&url, &module_path.display().to_string());
|
||||
rust_template::deploy_js_script_host(&url, &module_path.display().to_string());
|
||||
fs::write("deploy.js", deploy_script_host_str)?;
|
||||
std::process::Command::new("node")
|
||||
.arg("deploy.js")
|
||||
|
@ -3259,7 +3423,7 @@ fn shell(cfg_override: &ConfigOverride) -> Result<()> {
|
|||
}
|
||||
};
|
||||
let url = cluster_url(cfg, &cfg.test_validator);
|
||||
let js_code = template::node_shell(&url, &cfg.provider.wallet.to_string(), programs)?;
|
||||
let js_code = rust_template::node_shell(&url, &cfg.provider.wallet.to_string(), programs)?;
|
||||
let mut child = std::process::Command::new("node")
|
||||
.args(["-e", &js_code, "-i", "--experimental-repl-await"])
|
||||
.stdout(Stdio::inherit())
|
||||
|
@ -3309,7 +3473,7 @@ fn login(_cfg_override: &ConfigOverride, token: String) -> Result<()> {
|
|||
|
||||
// Freely overwrite the entire file since it's not used for anything else.
|
||||
let mut file = File::create("credentials")?;
|
||||
file.write_all(template::credentials(&token).as_bytes())?;
|
||||
file.write_all(rust_template::credentials(&token).as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -3388,7 +3552,7 @@ fn publish(
|
|||
}
|
||||
|
||||
// All workspace programs.
|
||||
for path in cfg.get_program_list()? {
|
||||
for path in cfg.get_rust_program_list()? {
|
||||
let mut dirs = walkdir::WalkDir::new(path)
|
||||
.into_iter()
|
||||
.filter_entry(|e| !is_hidden(e));
|
||||
|
@ -3674,6 +3838,7 @@ mod tests {
|
|||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
@ -3690,6 +3855,7 @@ mod tests {
|
|||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
@ -3706,6 +3872,7 @@ mod tests {
|
|||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,473 @@
|
|||
use crate::config::ProgramWorkspace;
|
||||
use crate::VERSION;
|
||||
use anchor_syn::idl::Idl;
|
||||
use anyhow::Result;
|
||||
use heck::{ToLowerCamelCase, ToSnakeCase, ToUpperCamelCase};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use std::fmt::Write;
|
||||
|
||||
pub fn default_program_id() -> Pubkey {
|
||||
"F1ipperKF9EfD821ZbbYjS319LXYiBmjhzkkf5a26rC"
|
||||
.parse()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn idl_ts(idl: &Idl) -> Result<String> {
|
||||
let mut idl = idl.clone();
|
||||
for acc in idl.accounts.iter_mut() {
|
||||
acc.name = acc.name.to_lower_camel_case();
|
||||
}
|
||||
let idl_json = serde_json::to_string_pretty(&idl)?;
|
||||
Ok(format!(
|
||||
r#"export type {} = {};
|
||||
|
||||
export const IDL: {} = {};
|
||||
"#,
|
||||
idl.name.to_upper_camel_case(),
|
||||
idl_json,
|
||||
idl.name.to_upper_camel_case(),
|
||||
idl_json
|
||||
))
|
||||
}
|
||||
|
||||
pub fn deploy_js_script_host(cluster_url: &str, script_path: &str) -> String {
|
||||
format!(
|
||||
r#"
|
||||
const anchor = require('@coral-xyz/anchor');
|
||||
|
||||
// Deploy script defined by the user.
|
||||
const userScript = require("{script_path}");
|
||||
|
||||
async function main() {{
|
||||
const url = "{cluster_url}";
|
||||
const preflightCommitment = 'recent';
|
||||
const connection = new anchor.web3.Connection(url, preflightCommitment);
|
||||
const wallet = anchor.Wallet.local();
|
||||
|
||||
const provider = new anchor.AnchorProvider(connection, wallet, {{
|
||||
preflightCommitment,
|
||||
commitment: 'recent',
|
||||
}});
|
||||
|
||||
// Run the user's deploy script.
|
||||
userScript(provider);
|
||||
}}
|
||||
main();
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
pub fn deploy_ts_script_host(cluster_url: &str, script_path: &str) -> String {
|
||||
format!(
|
||||
r#"import * as anchor from '@coral-xyz/anchor';
|
||||
|
||||
// Deploy script defined by the user.
|
||||
const userScript = require("{script_path}");
|
||||
|
||||
async function main() {{
|
||||
const url = "{cluster_url}";
|
||||
const preflightCommitment = 'recent';
|
||||
const connection = new anchor.web3.Connection(url, preflightCommitment);
|
||||
const wallet = anchor.Wallet.local();
|
||||
|
||||
const provider = new anchor.AnchorProvider(connection, wallet, {{
|
||||
preflightCommitment,
|
||||
commitment: 'recent',
|
||||
}});
|
||||
|
||||
// Run the user's deploy script.
|
||||
userScript(provider);
|
||||
}}
|
||||
main();
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
pub fn deploy_script() -> &'static str {
|
||||
r#"// 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("@coral-xyz/anchor");
|
||||
|
||||
module.exports = async function (provider) {
|
||||
// Configure client to use the provider.
|
||||
anchor.setProvider(provider);
|
||||
|
||||
// Add your deploy script here.
|
||||
};
|
||||
"#
|
||||
}
|
||||
|
||||
pub fn ts_deploy_script() -> &'static str {
|
||||
r#"// 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("@coral-xyz/anchor");
|
||||
|
||||
module.exports = async function (provider) {
|
||||
// Configure client to use the provider.
|
||||
anchor.setProvider(provider);
|
||||
|
||||
// Add your deploy script here.
|
||||
};
|
||||
"#
|
||||
}
|
||||
|
||||
pub fn solidity(name: &str) -> String {
|
||||
format!(
|
||||
r#"
|
||||
@program_id("{}")
|
||||
contract {} {{
|
||||
bool private value = true;
|
||||
|
||||
@payer(payer)
|
||||
constructor(address payer) {{
|
||||
print("Hello, World!");
|
||||
}}
|
||||
|
||||
/// A message that can be called on instantiated contracts.
|
||||
/// This one flips the value of the stored `bool` from `true`
|
||||
/// to `false` and vice versa.
|
||||
function flip() public {{
|
||||
value = !value;
|
||||
}}
|
||||
|
||||
/// Simply returns the current value of our `bool`.
|
||||
function get() public view returns (bool) {{
|
||||
return value;
|
||||
}}
|
||||
}}
|
||||
"#,
|
||||
default_program_id(),
|
||||
name.to_snake_case(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn mocha(name: &str) -> String {
|
||||
format!(
|
||||
r#"const anchor = require("@coral-xyz/anchor");
|
||||
|
||||
describe("{}", () => {{
|
||||
// Configure the client to use the local cluster.
|
||||
anchor.setProvider(anchor.AnchorProvider.env());
|
||||
|
||||
it("Is initialized!", async () => {{
|
||||
// Add your test here.
|
||||
const program = anchor.workspace.{};
|
||||
const tx = await program.methods.initialize().rpc();
|
||||
console.log("Your transaction signature", tx);
|
||||
|
||||
const val1 = await program.methods.get()
|
||||
.accounts({{ dataAccount: dataAccount.publicKey }})
|
||||
.view();
|
||||
|
||||
console.log("state", val1);
|
||||
|
||||
await program.methods.flip()
|
||||
.accounts({{ dataAccount: dataAccount.publicKey }})
|
||||
.rpc();
|
||||
|
||||
const val2 = await program.methods.get()
|
||||
.accounts({{ dataAccount: dataAccount.publicKey }})
|
||||
.view();
|
||||
|
||||
console.log("state", val2);
|
||||
}});
|
||||
}});
|
||||
"#,
|
||||
name,
|
||||
name.to_upper_camel_case(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn jest(name: &str) -> String {
|
||||
format!(
|
||||
r#"const anchor = require("@coral-xyz/anchor");
|
||||
|
||||
describe("{}", () => {{
|
||||
// Configure the client to use the local cluster.
|
||||
anchor.setProvider(anchor.AnchorProvider.env());
|
||||
|
||||
it("Is initialized!", async () => {{
|
||||
// Add your test here.
|
||||
const program = anchor.workspace.{};
|
||||
const tx = await program.methods.initialize().rpc();
|
||||
console.log("Your transaction signature", tx);
|
||||
}});
|
||||
}});
|
||||
"#,
|
||||
name,
|
||||
name.to_upper_camel_case(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn package_json(jest: bool) -> String {
|
||||
if jest {
|
||||
format!(
|
||||
r#"{{
|
||||
"scripts": {{
|
||||
"lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
|
||||
"lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
|
||||
}},
|
||||
"dependencies": {{
|
||||
"@coral-xyz/anchor": "^{VERSION}"
|
||||
}},
|
||||
"devDependencies": {{
|
||||
"jest": "^29.0.3",
|
||||
"prettier": "^2.6.2"
|
||||
}}
|
||||
}}
|
||||
"#
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
r#"{{
|
||||
"scripts": {{
|
||||
"lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
|
||||
"lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
|
||||
}},
|
||||
"dependencies": {{
|
||||
"@coral-xyz/anchor": "^{VERSION}"
|
||||
}},
|
||||
"devDependencies": {{
|
||||
"chai": "^4.3.4",
|
||||
"mocha": "^9.0.3",
|
||||
"prettier": "^2.6.2"
|
||||
}}
|
||||
}}
|
||||
"#
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ts_package_json(jest: bool) -> String {
|
||||
if jest {
|
||||
format!(
|
||||
r#"{{
|
||||
"scripts": {{
|
||||
"lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
|
||||
"lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
|
||||
}},
|
||||
"dependencies": {{
|
||||
"@coral-xyz/anchor": "^{VERSION}"
|
||||
}},
|
||||
"devDependencies": {{
|
||||
"@types/bn.js": "^5.1.0",
|
||||
"@types/jest": "^29.0.3",
|
||||
"jest": "^29.0.3",
|
||||
"prettier": "^2.6.2",
|
||||
"ts-jest": "^29.0.2",
|
||||
"typescript": "^4.3.5"
|
||||
}}
|
||||
}}
|
||||
"#
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
r#"{{
|
||||
"scripts": {{
|
||||
"lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
|
||||
"lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
|
||||
}},
|
||||
"dependencies": {{
|
||||
"@coral-xyz/anchor": "^{VERSION}"
|
||||
}},
|
||||
"devDependencies": {{
|
||||
"chai": "^4.3.4",
|
||||
"mocha": "^9.0.3",
|
||||
"ts-mocha": "^10.0.0",
|
||||
"@types/bn.js": "^5.1.0",
|
||||
"@types/chai": "^4.3.0",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"typescript": "^4.3.5",
|
||||
"prettier": "^2.6.2"
|
||||
}}
|
||||
}}
|
||||
"#
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ts_mocha(name: &str) -> String {
|
||||
format!(
|
||||
r#"import * as anchor from "@coral-xyz/anchor";
|
||||
import {{ Program }} from "@coral-xyz/anchor";
|
||||
import {{ {} }} from "../target/types/{}";
|
||||
|
||||
describe("{}", () => {{
|
||||
// Configure the client to use the local cluster.
|
||||
const provider = anchor.AnchorProvider.env();
|
||||
anchor.setProvider(provider);
|
||||
|
||||
const dataAccount = anchor.web3.Keypair.generate();
|
||||
const wallet = provider.wallet;
|
||||
|
||||
const program = anchor.workspace.{} as Program<{}>;
|
||||
|
||||
it("Is initialized!", async () => {{
|
||||
// Add your test here.
|
||||
const tx = await program.methods.new(wallet.publicKey)
|
||||
.accounts({{ dataAccount: dataAccount.publicKey }})
|
||||
.signers([dataAccount]).rpc();
|
||||
console.log("Your transaction signature", tx);
|
||||
|
||||
const val1 = await program.methods.get()
|
||||
.accounts({{ dataAccount: dataAccount.publicKey }})
|
||||
.view();
|
||||
|
||||
console.log("state", val1);
|
||||
|
||||
await program.methods.flip()
|
||||
.accounts({{ dataAccount: dataAccount.publicKey }})
|
||||
.rpc();
|
||||
|
||||
const val2 = await program.methods.get()
|
||||
.accounts({{ dataAccount: dataAccount.publicKey }})
|
||||
.view();
|
||||
|
||||
console.log("state", val2); }});
|
||||
}});
|
||||
"#,
|
||||
name.to_upper_camel_case(),
|
||||
name.to_snake_case(),
|
||||
name,
|
||||
name.to_upper_camel_case(),
|
||||
name.to_upper_camel_case(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn ts_jest(name: &str) -> String {
|
||||
format!(
|
||||
r#"import * as anchor from "@coral-xyz/anchor";
|
||||
import {{ Program }} from "@coral-xyz/anchor";
|
||||
import {{ {} }} from "../target/types/{}";
|
||||
|
||||
describe("{}", () => {{
|
||||
// Configure the client to use the local cluster.
|
||||
const provider = anchor.AnchorProvider.env();
|
||||
anchor.setProvider(provider);
|
||||
|
||||
const dataAccount = anchor.web3.Keypair.generate();
|
||||
const wallet = provider.wallet;
|
||||
|
||||
const program = anchor.workspace.{} as Program<{}>;
|
||||
|
||||
it("Is initialized!", async () => {{
|
||||
// Add your test here.
|
||||
const tx = await program.methods.new(wallet.publicKey)
|
||||
.accounts({{ dataAccount: dataAccount.publicKey }})
|
||||
.signers([dataAccount]).rpc();
|
||||
console.log("Your transaction signature", tx);
|
||||
}});
|
||||
}});
|
||||
"#,
|
||||
name.to_upper_camel_case(),
|
||||
name.to_snake_case(),
|
||||
name,
|
||||
name.to_upper_camel_case(),
|
||||
name.to_upper_camel_case(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn ts_config(jest: bool) -> &'static str {
|
||||
if jest {
|
||||
r#"{
|
||||
"compilerOptions": {
|
||||
"types": ["jest"],
|
||||
"typeRoots": ["./node_modules/@types"],
|
||||
"lib": ["es2015"],
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
||||
"#
|
||||
} else {
|
||||
r#"{
|
||||
"compilerOptions": {
|
||||
"types": ["mocha", "chai"],
|
||||
"typeRoots": ["./node_modules/@types"],
|
||||
"lib": ["es2015"],
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
||||
"#
|
||||
}
|
||||
}
|
||||
|
||||
pub fn git_ignore() -> &'static str {
|
||||
r#"
|
||||
.anchor
|
||||
.DS_Store
|
||||
target
|
||||
**/*.rs.bk
|
||||
node_modules
|
||||
test-ledger
|
||||
"#
|
||||
}
|
||||
|
||||
pub fn prettier_ignore() -> &'static str {
|
||||
r#"
|
||||
.anchor
|
||||
.DS_Store
|
||||
target
|
||||
node_modules
|
||||
dist
|
||||
build
|
||||
test-ledger
|
||||
"#
|
||||
}
|
||||
|
||||
pub fn node_shell(
|
||||
cluster_url: &str,
|
||||
wallet_path: &str,
|
||||
programs: Vec<ProgramWorkspace>,
|
||||
) -> Result<String> {
|
||||
let mut eval_string = format!(
|
||||
r#"
|
||||
const anchor = require('@coral-xyz/anchor');
|
||||
const web3 = anchor.web3;
|
||||
const PublicKey = anchor.web3.PublicKey;
|
||||
const Keypair = anchor.web3.Keypair;
|
||||
|
||||
const __wallet = new anchor.Wallet(
|
||||
Keypair.fromSecretKey(
|
||||
Buffer.from(
|
||||
JSON.parse(
|
||||
require('fs').readFileSync(
|
||||
"{wallet_path}",
|
||||
{{
|
||||
encoding: "utf-8",
|
||||
}},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
const __connection = new web3.Connection("{cluster_url}", "processed");
|
||||
const provider = new anchor.AnchorProvider(__connection, __wallet, {{
|
||||
commitment: "processed",
|
||||
preflightcommitment: "processed",
|
||||
}});
|
||||
anchor.setProvider(provider);
|
||||
"#
|
||||
);
|
||||
|
||||
for program in programs {
|
||||
write!(
|
||||
&mut eval_string,
|
||||
r#"
|
||||
anchor.workspace.{} = new anchor.Program({}, new PublicKey("{}"), provider);
|
||||
"#,
|
||||
program.name.to_upper_camel_case(),
|
||||
serde_json::to_string(&program.idl)?,
|
||||
program.program_id
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(eval_string)
|
||||
}
|
Loading…
Reference in New Issue